文是单位同事胡计平的一个关于效率优化的总结,内容很实用,转贴到blog里,以备自己日后查看,也希望能对更多的人有所帮助
最近写一程序,跟效率优化打上了交道,把其中的体会写下来,供大家讨论分享,我想效率优化工作可以分为如下几个步骤:
(1)查找影响效率的瓶颈之处:定位的方法当然是使用时间函数,一般精确的使用GetTickCount就可以,非常精确的使用
function GetCycleCount: Int64;
asm
RDTSC; //得到当前CPU的时钟周期数。
end;
想必这个知识大家都应该已经知道了。定位的过程大概是先定位函数,再到一条语句,大多数情况下,效率的问题就是一条语句引起的。不过有几点值得大家注意,比如在一个函数内使用时间函数测试出来的总时间为1s,然而在该函数调用处使用时间函数测试出来的总时间却为5s,这个时候如果你稍微疏忽,你很可能定位不到到底是哪里的问题。这里就涉及到了一些隐藏的地方,它们消耗了时间,你却不知道,详述将在下文。
(2)找到了瓶颈之处,接下来就是分析原因,这个过程可能会遇到很容易的,一看就知道了原因所在,但是更多情况下它会隐藏着,不易发现,需要我们的耐心,细心和信心。分析和查找问题的原因,我自己总结了下面这些方法或者说可以考虑的方面:
1.是否可以换个核心思想:对于类似解析器,编译器的程序,很有可能在现有的核心思想下现有的实现方式已经是最优了,几乎没有提高的余地,那么我们可以想想换个思想。比如XML解析器,微软提供了Dom,但是它的核心思想已经决定了它就只能那么快了,并且消耗内存巨大,大概是源文件的5-10倍,SAX的事件驱动模型也决定了它比Dom更快更少耗内存,但是新一代的XML解析器VTD-XML(完全基于字节流解析,没有对象)也注定了它比所有其他的XML解析器具有更多的优势!
2.算法代码编写是否正确:前阵子,姚柯跟我说,公共资源的排序类中有一个条件写反了,使得该使用二分查找的地方使用了普通查找,修改后发现构造交叉表的效率提高10倍以上,整体查询效率提高7-8倍,从这个事件中我们可以看到考虑算法代码正确与否是很有必要的,有时候问题就隐藏在其中,而且隐藏得很深!
3.算法选择是否恰当:很明显,如果你使用了冒泡排序,怎么也快不了。当然大部分情况不会是这样的,更多情况是要考虑当前情况和环境来选择的,很有可能平时我们认为效率基本一样的多种算法,但是在当前情况下可能就只有一种最恰当,但不幸地是之前我们选择错了,那么现在你就有了改进的机会!
4.是否不必要的循环次数过多:有时候多出很多不必要的循环,但是不易发现,我们在优化的时候,这是个重点考虑的对象,除非你已经极其肯定就是要运行这么多次,否则要一次又一次的怀疑自己的观点。
5.关键函数中是否有很多的string参数:我说的关键函数是指调用次数很多。关键函数中最好不要申明string,动态数组之类的临时变量,因为它们在该函数最开始有隐藏的初始化,在函数结尾有隐藏的释放。我曾经在一个关键函数中申明了一个动态数组,测试效率的时候,在定位的时候就出现了(1)中提到的现象,原来是临时变量导致的,最后改造函数,去掉临时变量,函数效率马上提高一倍!
6.是否因为派生机制导致效率问题:我在写一个程序的时候,其中涉及到了派生关系,由于派生体系中的一个方法A被使用者调用的次数非常多,达百万次以上。开始的时候也是优化函数内部,但是后来发现函数内部不可能再优化了,但是整体效率还是差一些,于是苦思冥想,发现我用了派生机制,每次调用该方法的时候,都要去VMT表查找,消耗了一些时间,不要小看这个,当调用次数巨大的时候,你会发现这个原因影响很大。当我去掉了派生关系,直接用静态方法的时候,整体效率提高一倍多,不得不说让人欣喜!
7.是否接口使用不恰当:接口使用不恰当可能的原因有:你对此不熟悉;接口本身提供的就有迷惑性。比如你对公司的平台不熟悉,那么很可能出现使用不当,导致效率出现问题,这就是典型的不熟悉的原因;我最近做一个跟word有关系的程序,其中一个要求就是得到word的文档结构图。在遍历word编程接口中的Paragraphs接口时,开始看了接口原型,发现有Count,Item(AIndex)等属性和方法,于是就用了for循环遍历,但是测试效率时发现当Count有2000多个的时候,居然需要180s才能遍历完成!我怎么也不相信这么慢,于是开始定位,发现居然是iParagraph := iParagraphs.Item(I)这行代码巨慢,它用了160s。但是我不太相信微软提供的遍历接口会有这么慢,于是去接口原型中寻找其他途径,果然我有了新发现,在Paragraph接口中有Next,Previous方法,所以可以进行迭代式遍历,换成迭代式访问后,马上只需要20s了,所以估计它内部是链式存储。事后想,象微软提供的这样的接口,真的很有迷惑性,两种遍历方式,但是相差甚远!值得我们思考和注意!
(3)找到了原因之后,我想较多情况下还是可以找到解决问题的办法,当然也有很多不好解决的,就象上面(2)提到的第一点原因。还是那句话,更多的是需要我们更耐心,细心和信心,当然开阔的思维和思考是最基础,最重要的!