这将是很短的一章,因为关于性能优化,Unix 的经验告诉我们最主要的就是如何知道何时不去优化。其次,最有效的优化往往是优化之外的其它事情,如:清晰干净的设计。

#12.1 什么都别做,就站在那儿

程序员工具箱中最强大的优化技术就是不做优化。

有几个理由支持这项禅式的忠告。其中一个是摩尔定律的指数效应一一最聪明、最便宜、常常也是最迅速的性能提升方法,就是等上几个月,期望硬件性能更好。

  • 考虑到硬件和程序员时间成本比率,总是有更值得打发时间的事,别去优化一个工作中的系统

这是有数学上的理由的。如果仅仅只是为了减少资源使用的一个常数部分而优化,那是很不值得的。更明智的做法是集中精力将时间复杂度或空间复杂度从 O(n2)O(n^2) 降至 O(n)O(n)O(nlogn)O(n logn) 或者类似的,从一个更高次的指数降下来。线性性能增益往往很快就会被摩尔定律覆盖了。

#12.2 先估量,后优化

如果有真凭实据证明应用程序运行缓慢,这时 (仅当此时)才可以考虑优化代码但付诸实施前,要先估量。

最初的 Unix 程序员最先学到的经验之一就是要明确瓶颈所在,直觉实在是个糟糕的向导,即使特别熟悉可疑代码的人也不例外。

#12.3 非定域性之害

最有效的代码优化方法就是保持代码短小简单。

随着机器资源成本的直线下降,庞大数据结构的平均开销也随之而降一一但是因为相邻级别缓存的切换开销上升了,大型结构突破缓存容量对性能的影响也就增加了。 因此在这里,“小即是美”的建议比以往更有用,尤其是考虑到核心数据结构必须留在最快的缓存里。该建议也同样适用于代码:通常,指令加载要比执行花费的时间更多。

这彻底推翻了某些传统的建议。如循环分解,去掉相对昂贵的机器指令而增加代码的总行数,那是不值得的。在 3D 图形引擎中,优化旋转操作的 sin(x)sin(x) 函数表在现代机器中占据 365×4365 \times 4 字节的空间在。处理器缓冲速度没有内存查询快时,这显然是个速度优化。但是现在,比起函数表产生的附加 Cache Miss 的可能开销,每次重新计算可能更快。

但在将来,也许随着缓存的增大又会反过来。更普遍的是,不妨悲观地认为,许多优化方法都是暂时的而且常常随着成本比例而变化。唯一的可行方案就是先衡量后再优化。

#12.4 吞吐量和延迟

#12.4.3 缓存操作结果

认为迫切需要缓存的时候,明智的做法是能够从更深层次来考虑,并问问为什么缓存是必须的。这比将缓存的所有边界条件都考虑到要容易得多。