17. 对于基本数据类型,优先使用+?、?-?、?*?、?和?/,而不是+=?、?-=?、?*= 和 /=
  18. 推迟定义本地变量
  定义一个对象变量通常需要调用一次函数(构造函数)。
  如果一个变量只在某些情况下需要(例如在一个if声明语句内),仅在其需要的时候定义,这样,构造函数仅在其被使用的时候调用。
  19. 对于对象,使用前缀操作符(++obj),而不是后缀操作符(obj++)
  这在你的射线追踪算法中可能不是一个问题
  使用后缀操作符需要执行一次对象拷贝(这也导致了额外的构造和析构函数调用),而前缀的构造函数不需要一个临时的拷贝。
  20. 小心使用模板
  对不同的是实例实现进行不同的优化。
  标准模板库已经经过良好的优化,不过我建议你在实现一个交互式射线追踪算法时避免使用它。
  使用自己的实现,你知道它如何使用算法,所以你知道如何有效的实现它。
  重要的是,我的经历告诉我:调试STL库非常低效。通常这也不是一个问题,除非你使用debug版本做性能分析。你会发现STL的构造函数,迭代器和其他一些操作,占用了你15%的运行时间,这会导致你分析性能输出更加费劲。
  21. 避免在计算时进行动态内存分配
  动态内存对于存储场景和运行期间其他数据都很有用。
  但是,在许多(大多数)的系统动态内存分配需要获取控制访问分配器的锁。对于多线程应用程序,现实中使用动态内存由于额外的处理器导致了性能下降,因为需要等待分配器锁和释放内存。
  即便对于单线程应用,在堆上分配内存也比在栈上分配内存开销大得多。操作系统还需要执行一些操作来计算并找到适合尺寸的内存块。
  22. 找到你系统内存cache的信息并利用它们
  如果一个是数据结构正好适合一个cache行,处理整个类从内存中只需要做一次获取操作。
  确保所有的数据结构都是cache行大小对齐(如果你的数据结构和一个cache行大小都是128字节,仍有可能因为你的结构体中的一个字节在一个cache行中,而其他127字节在另外一个cahce行中)。
  23. 避免不需要的数据初始化
  如果你需要初始化一大段的内存,考虑使用memset。
  24. 尽早结束循环和尽早返回函数调用
  考虑一个射线和三角形交叉,通常的情况是射线会越过三角,所以这里可以优化。
  如果你决定将射线和三角面板交叉。如果射线和面板交叉t值是负数,你可以立即返回。这允许你跳过射线三角交叉一大半的质心坐标计算。这是一个大的节约,一旦你知道这个交叉不存在,你应该立即返回交叉计算函数。
  同样的,一些循环也应该尽早结束。例如,当设置阴影射线,对于近处的交叉通常都是不必须的,一旦有类似的的交叉,交叉计算应该尽早返回。(这里的交叉含义不太明白,可能是专业词汇,译者注)
  25. 在稿纸上简化你的方程式
  许多方程式中,通常都可以或者在某些条件中取消计算。
  编译器不能发现这些简化,但是你可以。取消一个内部循环的一些昂贵操作可以抵消你在其他地方的好几天的优化工作。
  26. 整数、定点数、32位浮点数和64位双精度数字的数学运算差异,没有你想象的那么大
  在现代CPU,浮点数运算和整数运算差不多拥有同样的效率。在计算密集型应用(比如射线追踪),这意味这可以忽略整数和浮点数计算的开销差异。这也是说,你不必要对算数进行整数处理优化。
  双精度浮点数运算也不比单精度浮点数运算更慢,尤其是在64位机器上。我在同一台机器测试射线追踪算法全部使用double比全部使用floats运行有时候更快,反过来测试也看到了一样的现象(这里的原文是:I have seen ray tracers run faster using all doubles than all floats on the same machine. I have also seen the reverse)。
  27. 不断改进你的数学计算,以消除昂贵的操作
  sqrt()经常可以被优化掉,尤其是在比较两个值的平方根是否一致时。
  如果你重复地需要处理 除x 操作,考虑计算1/x的值,乘以它。这在向量规范化(3次除法)运算中赢得了大的改进,不过我近发现也有点难以确定的。不过,这仍然有所改进,如果你要进行三次或更多除法运算。
  如果你在执行一个循环,那些在循环中执行不发生变化的部分,确保提取到循环外部。
  考虑看看你的计算值是否可以在循环中修改得到(而不每次都重新开始循环计算)。