zhouqijie

效率



80-20法则

一个程序80%的资源用于20%的代码身上。

包括:

  1. 80%的执行时间花在20%的代码身上。
  2. 80%的内存被20%的代码使用。
  3. 80%的磁盘访问动作被20%的代码执行。
  4. 80%的维护力气花在20%的代码上面。

要找到性能的瓶颈,可行之道就是完全根据观察或实验来识别出造成你心痛的那20%代码,而可行之道就是借助某个程序分析器(profiler)。



考虑使用缓式评估(lazy evaluation)

lazy evaluation 在可在多种场合派上用场。

在你真正需要之前,不必着急为某物做一个副本。取而代之的是,以延迟战术应付–只要能够,就使用其他副本。在某些应用领域,你常有可能永远不需要提供那样一个副本。

在产生一个大对象的时候,只产生该对象的“外壳”,不从磁盘读取任何字段数据。当对象内某个字段被需要了,程序才从数据库取回对应的数据。

对象内每个字段都是指针,指向必要的数据,而构造函数负责将每个字段初始化为null,这样的null象征着字段数据尚未从数据库读取。

Matrix m1(1000, 1000);//非常大的1000x1000矩阵
Matrix m2(1000, 1000);//非常大的1000x1000矩阵

Matrix m3 = m1 + m2;//这一行是不用求值的!    
Matrix m4(1000, 1000);
m3 = m4 * m1;

try_visit(m3);
//m3的数据结构可能只是两个矩阵指针(指向操作数)和一个enum(值为运算类型)。    
//只有当m3被用到时,才真正对m3进行求值。    



分期摊还预期的计算成本

让程序超前进度地做“要求以外”的更多工作。此条款背后的哲学可称为超急评估(over-eager evaluation):在被要求之前就先把事情做下去。

over-eager evaluation 背后的观念是,如果你预期程序常常会用到某个计算,你可以降低每次计算的成本,办法就是设计一份数据结构以便能够极有效地处理需求。

Cache:将已经计算好而有可能再被需要的数值保留下来。
Prefetch:一次读取一大块数据比多次读取小块数据更快。此外经验显示,如果某处数据被需要,通常其邻近的数据也会被需要。



临时对象优化

C++的真正临时对象是不可见的–不会在源代码中出现,代码中定义的名为”temp”的这样的对象只是一个局部对象。

只要你产生一个non-heap对象而没有为它命名,便诞生了一个临时对象。这样的匿名对象通常发生于两种情况。

  1. 当隐式类型转换被施行起来以求函数调用能够成功。(CRE:实参类型隐式转换为形参类型)(仅当值传递以及const引用传递时)
  2. 当函数返回对象的时候。(可以RVO优化)

返回所谓的constructor arguments以取代对象。

不过,增加一大堆重载函数不一定是好事,除非你有好的理由相信,使用重载函数后,程序的整体效率能获得重大改善。

一般而言,复合操作符比起对应的单独版本效率更高,因为单独版本通常必须返回一个新对象,而我们必须因此负担一个临时对象的析构和构造成本。而复合版本则是直接将结果写入左端自变量,不需要产生临时对象。

可以将复合形式为基础来实现单独形式。(CRE:例如operator+调用operator+=



考虑使用其他程序库

不同的程序库即使提供相同的机能,也往往表现出不同的性能取舍策略,所以一旦你(通过profiler)找出程序的瓶颈,你应该思考是否有可能因为改用另一个程序库而消除了那些瓶颈。



了解虚函数、多继承、虚基类、RTTI的成本

参考《C++对象模型》。

(END)