您的位置:首页 > 编程语言 > C语言/C++

优化C/C++代码的小技巧

2014-12-04 16:48 211 查看

无意看到一篇小短文,猜测作者应该是一个图形学领域的程序员或专家,介绍了在光线(射线)追踪程序中是如何优化C/C++代码的。倒也有一些参考意义,当然有的地方我并不赞同或者说我也不完全理解,原文在此,我的粗糙翻译如下:
  1. 牢记Ahmdal定律



  funccost表示是函数func的运行时间百分比,funcspeedup是你优化后函数的运行系数;
  所以,如果函数TriangleIntersect()占用40%的运行时间,而在你优化后使它运行快了两倍,那么你的程序运行能够快了25%;
  这意味着不经常使用的代码不需要做过多优化(或者完全不优化),比如场景加载过程;
  也就是:让频繁调用的代码运行得更加高效,而让较少调用的代码保持运行正确;
  2. 先有正确的代码,然后再做优化
  这并不是说先花8个周时间写一个全功能的光线追踪器,然后再花8个周去优化;
  而是在你的管线追踪程序中的多个阶段都进行优化;
  如果代码是正确的,而你又知道哪些函数会被频繁的调用,优化是很明显的;
  然后找到瓶颈所在,并去除瓶颈(通过优化或者算法改进)。通常来说改进算法可以很显著地优化瓶颈——甚至可能采用了一个你没想到的算法。优化那些你所知道的将被频繁调用的函数是一个很好的做法;
  3. 那些我认识的能够写出非常高效的代码的人说,他们花费在优化代码上的时间是他们写代码时间的至少两倍以上
  4. 跳转/分支语句是昂贵的,不管何时尽可能的减少使用
  函数调用除了栈存储操作外,还需要两次跳转;
  优先选择迭代,而不是递归;
  如果是短函数,使用内联来消除函数开销;
  将循环放在函数内(例如将for(i=0;i<100;i++) DoSomething();改为在DoSomething()内做DoSomething());
  长长的if...else if...else if...else if...语句链需要大量的跳转才能结束(除了在测试每个条件时)。如果可能,改为switch语句,有时编译器可以有优化为在一个表中查找和单级跳转。如果switch语句是不可能的,那把最经常走到的if语句放在语句链开头;
  5. 考虑数组索引的顺序
  两维或更多维的数组在内存中仍是按一维存储的。这意思是array[i][j]和 array[i][j+1]是相邻的(C/C++代码),然而array[i][j]和array[i+1][j]却可以相离的任意远;
  访问物理内存中的连续数据,可以显著加快你的代码(有时是一个数量级,甚至更多);
  现在CPU从主内存中加载数据到高速缓存时,它不仅仅是只加载单一数据,而是加载一块数据,既包含了要请求的数据,也包含部分相邻数据(一个cache行)。这意思是说如果array[i][j]在CPU缓存中,那么array[i][j+1]就很有可能也在缓存中了,然而array[i+1][j]可能仍在内存中;
  6. 考虑指令级并行性(IPL)
  尽管很多程序仍是单线程执行,但现代的CPU已经能够在单核上有显著的并行性。这意味着单CPU也可能同时执行4个浮点数乘法、等待4个内存请求,并执行即将到来的分支比较操作
  为了充分利用这种并行性,代码块(比如在跳转语句中)需要足够的独立指令来使CPU得到充分使用;
  可以考虑通过展开循环来改进;
  这也是使用内联函数的一个很好的原因;
  7. 避免或减少局部变量的使用
  局部变量通常是存储在栈上。如果很少,可以存储在寄存器中。在这种情况下,函数不仅得到了对存储在寄存器上的数据的更快内存访问的好处,也可以避免建立一个栈帧的开销;
  但是,也不要把所有对象都全盘声明为全局变量;
  8. 减少函数参数的个数
  和减少局部变量的原因一样——他们也是在栈上存储的;
  9. 结构体(包括类)传参时使用传引用而不是传值
  在光线追踪程序中,哪怕是简单如vector、points、colors等结构,我也没有见过使用值传递的代码
  10. 如果你不需要一个函数的返回值,那就不要返回
  11. 尽可能避免使用转型操作
  整数和浮点数的指令集通常在不同的寄存器上运算,因此转型操作需要拷贝操作;
  短整形(char和short)仍然需要一个全尺寸的寄存器,而且在存储回内存之前,它们需要对齐到32位或64位上,然后才转换成更小尺寸类型;
  12. 当定义C++对象时一定要小心
  使用初始化(Color c(black))而不是赋值(Color c, c = black),而前者更快;
  13. 使类的默认构造函数尽可能的轻量
  特别是那简单的、经常使用的类(例如,颜色,矢量,点等);
  这些默认构造函数通常是在你不注意时就调用,甚至那时你并不希望这样;
  使用构造初始化列表(使用Color::Color() : r(0), g(0), b(0) {}而不是Color::Color() { r = g = b = 0; } );
  14. 尽可能使用移位操作符>>和<<,而不是整数乘法和除法
  15. 小心使用查表功能
  很多人鼓励对于复杂的功能(例如,三角函数)使用预先计算过值的查表法。对于光线跟踪程序来说,这往往是不必要的。内存查找是非常(日益)昂贵的,而且重新计算三角函数往往和从内存中查找值一样快(尤其是当你考虑到内存查找会影响CPU缓存命中率时);
  在其它情况下,查表可能是非常有用的。比如在GPU编程中,查表法通常是复杂功能的优先选择;
文章出处:http://www.51testing.com/html/19/n-866519.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: