循环
循环的效率取决于微处理器对循环控制分支的预测能力。一个具有一个较小并且固定的重复计数,没有分支的循环,可以完美地被预测。
循环展开
展开前
int i; for (i = 0; i < 20; i++) { if (i % 2 == 0); FuncA(i); else FuncB(i); FuncC(i); }
展开后
int i; for (i = 0; i < 20; i+=2) { FuncA(i); FuncC(i); FuncB(i+1); FuncC(i+1); }
这样做的好处:
- 循环次数变成了10次而不是20次,CPU可以更完美的进行预测
- if分支被消除,有利于编译器自动进行向量化等优化
循环展开的坏处:
- 展开循环后在代码缓存中占用更多空间
- 非常小的循环展开不如不展开
- 如果重复计数为奇数,并将其展开为2, 则必须在循环之外执行额外的迭代。
只有在能够取得特定好处的情况下,才应该使用循环展开。如果一个循环包含浮点运算,且循环计数器是整数,那么通常可 以假设整个计算时间是由浮点代码决定的,而不是由循环控制分支决定的。在这种情况下,展开循环是没有任何好处的 。
循环控制条件
如果循环控制分支依赖于循环内部的计算,则效率较低。
确定最坏情况下的最大重复计数并始终使用此迭代次数的效率会更高。
循环计数器最好是整数。
复制或清除数组
对于诸如复制数组或将数组中的元素全部设置为零这样的琐碎任务,使用循环可能不是最佳选择。使用memset和memcpy函数通常会更快
函数
函数调用会让程序慢下来,因为
- 代码地址跳转,可能需要4个时钟周期
- 如果代码分散在内存中会降低代码缓存效率
- 如果函数参数不够放在寄存器中,需要入到栈中,效率不高
- 需要额外时间设置stack frame, 保存和恢复寄存器
- 每个函数调用语句需要在分支目标缓冲区(BTB)中占用空间 , BTB争用可能会导致分支预测失败
如何避免函数调用降低效率呢?
避免不必要函数
不要过度封装
使用内联函数
如果函数很小,或者只在程序中的一个位置调用它,那么内联函数是有好处的。小函数通常由编译器自动内联
避免在最内层循环嵌套函数调用
如果程序关键的最内层循环包含对帧函数的调用,那么代码有可能通过内联帧函数或使帧函数调用的所有函数内联(把帧函数变为叶函数)来提升效率
使用宏代替函数
但是不要滥用宏,宏的问题是:名称不能重载或限制作用区域。宏将干扰具有相同名称的任何函数或变量,而与作用域或命名空间无关
使函数局部化
应该使同一个模块中使用的函数(即当前*.cpp* 文件)是局部的。 这使得编译器更容易将函数内联,并对函数调用进行优 化。
如何使函数局部化呢?
- 对于非类成员函数,直接使用static
- 对于类成员函数,将函数或类放置于匿名命名空间中
使用全程序优化
一些编译器具有对整个程序进行优化的选项,也可以选择将多个 .cpp 文件组合成一个对象文件。这使得编译器能够在组成程 序的所有 .cpp 模块之间优化寄存器分配和参数传递。
使用64位模式
现在服务器端开发都是64位模式了吧