在华容道程序写完后,我想尽办法优化程序的执行效率。衡量的尺子一直是河北石家庄李智广的"华容道全能版 V1.1",因此优化到5、6秒的执行时间就已经很满足了。期间的性能优化方法包括:
- 尽量减少CircularLinkedList中的节点数
- 去掉无用的接口,合并命名空间。避免接口转换对效率的影响
- 将排序算法代码调整为静态方法以提高调用效率。
这些对性能的调优使得代码的执行时间缩短了1.5~2秒左右。但"智能算法爱好者"在对华容道系列文章的评论中指出,他的程序执行时间最多0.571秒。这让我的心又一下子收起来了,5~6秒的效率损失发生在程序的什么地方呢?
经过仔细排查后,我意外发现这5~6秒的效率瓶颈居然在Exception上!在修正华容道程序之前我们先用一段代码测试一下Exception的效率:
public class CallTest
{
public bool ReturnTest(int n)
{
if(n>50)
return true;
else
return false;
}
public void ExceptTest(int n)
{
if(n>50)
throw new Exception("Too big number");
return;
}
}
public class MyApp
{
public static Random r = new Random();
public static CallTest c = new CallTest();
public static void Main()
{
ReturnCallTest();
ExceptCallTest();
return;
}
public static void ReturnCallTest()
{
DateTime begin = DateTime.Now;
for(int i=0; i<100000; i++)
{
try
{
c.ReturnTest((int)(r.NextDouble()*1000));
}
catch
{}
}
DateTime end = DateTime.Now;
TimeSpan t = end - begin;
Console.WriteLine("Return Call, Time used: {0}", t.TotalMilliseconds);
return;
}
public static void ExceptCallTest()
{
DateTime begin = DateTime.Now;
for(int i=0; i<100000; i++)
{
try
{
c.ExceptTest((int)(r.NextDouble()*1000));
}
catch
{}
}
DateTime end = DateTime.Now;
TimeSpan t = end - begin;
Console.WriteLine("Exception Call, Time used: {0}", t.TotalMilliseconds);
return;
}
}
程序有很多重复的代码,就不再重构了。关键是测试了Exception拦截异常的效率与通过返回值正常返回的效率差异。在我的机器上程序的执行结果如下:
Return Call, Time used: 10.0144
Exception Call, Time used: 2113.0384
两者之间的效率差异竟然在200倍以上甚至更多!再来看看我的华容道代码吧:AVLTree是不允许出现重复值的,在我的代码中不允许出现重复值是通过触发异常以及异常检验实现的,下面是原有程序的代码设计:
// AVLTree 中的部分代码
//======================================
…………
else if(root.info == newNode.info)
throw new Exception("No duplicates are allowed.");
…………
//======================================
// Mediator 中的部分代码
//======================================
public void CheckStep(ChessStep cStep)
{
try
{
_avlTree.Insert(cStep.layout);
_circleList.ConfirmAllocation();
_treeList.insertNode(cStep);
}
catch
{
}
}
可以看到在Mediator中,首先向AVLTree中插入节点,如果重复则触发异常,程序就落入catch中(也就是什么也不做)。这样ConfirmAllocation以及后面的操作就被跳过了,但这正是我需要的。没想到这么做带来了如此的效率损失。
现在我们对上面的代码进行一些微小的调整,首先将触发异常的代码改为返回一个bool型结果标识是否发生重复。然后对Mediaor再修改一下:
{
try
{
if(_avlTree.Insert(cStep.layout))
{
_circleList.ConfirmAllocation();
_treeList.insertNode(cStep);
}
}
catch
{
}
}
调整完成后,我的程序再对WinHRD程序第4关求解时,时间从原来的6.67秒一下子缩短到了1.55秒。尽管离"智能算法爱好者"的0.57秒的执行时间仍然相差甚远,但对于我的程序确实是一个非常大的变化。所有这些都归于Exception的调用效率上。