📄 item_008.htm
字号:
<p class=MsoNormal><span lang=EN-US><span style='mso-tab-count:1'> </span></span><i
style='mso-bidi-font-style:normal'><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>过早的优化是一切罪恶的根源。</span></i><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>——</span><span lang=EN-US>Donald Knuth [</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>引用</span><span lang=EN-US>Hoare</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的话</span><span lang=EN-US>]</span></p>
<p class=MsoNormal><span lang=EN-US><span style='mso-tab-count:2'> </span></span><i
style='mso-bidi-font-style:normal'><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>而另一方面,我们无法忽视效率。</span></i><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>——</span><span lang=EN-US>Jon Bentley</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span lang=EN-US>Hoare</span><span style='font-family:宋体;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>和</span><span
lang=EN-US>Knuth</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>是,当然的,始终的,完全的正确(参见第</span><span
lang=EN-US>6</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>条和本条)。而</span><span lang=EN-US>Bentley</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>也一样(参见第</span><span lang=EN-US>9</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>条)。</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>我们把过早的优化定义为:在没有确凿的证据证实对性能的需求的情况下(比如实际的测量,以及与目标性能进行比较),以性能为名使设计或代码变得更复杂,并导致可读性变差,因此根据定义并没有给程序增加实际的价值。所有过于频繁,不必要的,未经测量的优化努力甚至不会使程序变得更快。</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>始终记住:</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span lang=EN-US><span style='mso-tab-count:1'> </span><b
style='mso-bidi-font-weight:normal'><span style='mso-tab-count:1'> </span></b></span><b
style='mso-bidi-font-weight:normal'><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>使一个正确的程序变快要比使一个快的程序变得正确要远远容易得多。</span><span
lang=EN-US><o:p></o:p></span></b></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>因此,在默认的情况下,不要集中精力使代码变快;要首先集中精力使代码尽可能地清晰和可读(参见第</span><span
lang=EN-US>6</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>条)。清晰的代码更容易编写正确,更容易理解,更容易重构——也更容易优化。而复杂性,包括优化,总是可以在后来再引入——而且仅在必要的时候。</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>为什么过早的优化甚至经常不会使程序变得更快,主要原因有二。首先,在估计哪些代码会更快或更小,以及代码中的瓶颈会在哪里这方面,我们程序员是众所周知的差。这包括本书的作者,也包括你。考虑一下:当代计算机的特征是极端复杂的计算模型,这通常包括多个具有流水线的处理单元并行工作,一个深缓存层次(</span><span
lang=EN-US>deep cache hierarchy</span><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>),预测执行(</span><span
lang=EN-US>speculative execution</span><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>),分支预测(</span><span
lang=EN-US>branch prediction</span><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>)</span><span
lang=EN-US>…</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>而这仅仅只是</span><span lang=EN-US>CPU</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>。编译器位于硬件的上一层,为了把你的代码转换为能够充分利用硬件的机器指令,它们尽了最大努力来猜测。而位于所有这些复杂性之上的,是</span><span
lang=EN-US>…</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>嗯,是你的代码。所以如果你仅凭猜测就做优化的话,那么你的定位不明确的微优化(</span><span
lang=EN-US>micro-optimization</span><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>)几乎不可能显著地改善性能。因此,优化之前必须要进行测量;而测量之前必须要设定优化的目标。在证实有这样的需求之前,你应该集中精力于</span><span
lang=EN-US>#1</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的优先级——为人编写代码。(如果有人要求你做优化,<i
style='mso-bidi-font-style:normal'>一定要</i>他们给出证据。)</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>其次,在当代的程序中,越来越多的操作并非受限于</span><span
lang=EN-US>CPU</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>。它们可能受限于内存,受限于网络,受限于磁盘,等待网络服务(</span><span
lang=EN-US>web service</span><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>),或等待数据库。调整此类操作的代码最多也只是使这些操作以更快的速度等待。这同时意味着程序员浪费了宝贵的时间去做一些并不需要的改进,而不是做一些能够给程序增添价值的改进。</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>当然,总有一天你的确需要优化一些代码。在优化的时候,先考虑算法上的优化(参见第</span><span
lang=EN-US>7</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>条),尽量把所做的优化封装起来并使之模块化(例如:在一个函数或类中;参见第</span><span
lang=EN-US>5</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>条和第</span><span lang=EN-US>11</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>条),在注释中明确说明优化的原因以及所用算法的参考资料。</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>新手常犯的一个错误就是编写新的代码并念念不忘代码优化——怀着骄傲的心情!——以易理解性为代价。这样做在更多的——而不是更少的——时候会产生一大堆的意大利面条似的代码,它们即使在一开始是正确的,也难以阅读和修改。(参见第</span><span
lang=EN-US>6</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>条。)</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>下面这些不是过早的优化:按引用传递参数(参见第</span><span
lang=EN-US>25</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>条),优先调用前缀版本的</span><span lang=EN-US>++</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>和</span><span lang=EN-US>--</span><span style='font-family:
宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>(参见第</span><span
lang=EN-US>28</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>条),以及应该从我们的指尖自然地流出的类似惯用法。这些不是过早的优化;它们只是避免过早的退而求次(参见第</span><span
lang=EN-US>9</span><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>条)。</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><b style='mso-bidi-font-weight:normal'><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>示例</span></b></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><i style='mso-bidi-font-style:normal'><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>示例:</span><b style='mso-bidi-font-weight:normal'><span
lang=EN-US>inline</span></b></i><i style='mso-bidi-font-style:normal'><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的讽刺。</span></i><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>这里有一个简单的证明,有关于过早的微优化的隐性开销:性能剖析器(</span><span
lang=EN-US>profiler</span><span style='font-family:宋体;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>)非常善于告诉你,以函数调用次数的形式,哪些函数应该被标记为内嵌而实际上却没有;但另一方面性能剖析器却非常糟糕,它无法告诉你哪些函数不应该被标记为内嵌而实际上却被标记了。有太多的程序员以性能为名“在默认的情况下使用内嵌”,这几乎总是以更紧密的耦合为代价,而换来的最多也不过是一些并不确定的好处。(这里假定在你的编译器上编写</span><b
style='mso-bidi-font-weight:normal'><span lang=EN-US>inline</span></b><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>还能起作用。参见</span><span lang=EN-US>[Sutter00]</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>,</span><span lang=EN-US>[Sutter02]</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>,以及</span><span lang=EN-US>[Sutter04]</span><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>)</span></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><b style='mso-bidi-font-weight:normal'><span
style='font-family:宋体;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>例外</span></b></p>
<p class=MsoNormal><span lang=EN-US><o:p> </o:p></span></p>
<p class=MsoNormal><span style='font-family:宋体;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>在编写程序库时,很难预测哪些操作最终会被用于对性能敏感的代码中。但是在做晦涩的优化之前,即便是程序库的作者也会对一个大范围的客户代码运行性能测试。</span></p>
</div>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -