对我来说,Knuth 引用的关键方面是“一分钱一分货”。这就是他最终描述过早的优化器的方式——当有英镑要节省时,有人为了节省便士而讨价还价,并努力维护他们的“优化”(注意他在这里如何使用引号)软件。
我发现很多人经常只引用 Knuth 论文的一小部分。值得注意的是,他的论文主张使用 goto 来加快软件中的关键执行路径。
更完整的引用:
[...] 如果 n 的平均值约为 20,并且如果在程序中执行大约一百万次左右的搜索例程,这会显着节省整体运行速度。这种循环优化[使用 gotos] 并不难学习,而且正如我所说,它们只适用于程序的一小部分,但它们通常会产生大量节省。 [...]
当今许多软件工程师所共有的传统观念要求忽略小规模的效率。但我相信
这只是对他们看到的滥用行为的过度反应
由无法调试或
维护他们的“优化”程序。在已建立的工程
纪律 12% 的改进,很容易获得,从不考虑
边缘;我相信同样的观点应该在软件中盛行
工程。当然我不会费心做这样的优化
一个一次性的工作,但是当它是一个准备质量程序的问题时,
我不想将自己限制在拒绝我这样的工具上
效率。
毫无疑问,效率的圣杯会导致滥用。
程序员浪费大量时间思考或担忧
关于,他们程序的非关键部分的速度,以及这些
提高效率的尝试实际上会产生强烈的负面影响
考虑调试和维护。我们应该忘记小
效率,比如说 97% 的时间;过早优化是根本
万恶之源。
先验地判断一个项目的哪些部分通常是错误的
程序真的很关键,因为普遍的经验
一直在使用测量工具的程序员
直觉猜测失败。在使用这些工具七年之后,我确信从现在开始编写的所有编译器都应该设计为向所有程序员提供反馈,表明他们程序的哪些部分成本最高;事实上,这个反馈应该是自动提供的,除非它被特别关闭。
在程序员知道他的例程的哪些部分真正重要之后,像加倍循环这样的转换将是值得的。请注意,这种转换引入了go to 语句——其他几个循环优化也是如此。
所以这来自一个实际上非常关注微观层面性能的人,并且当时(优化器现在已经变得更好了),正在利用goto 来提高速度。
Knuth 建立“过早优化器”的核心是:
- 基于预感/迷信/人类直觉进行优化,没有过去的经验或衡量标准(盲目地优化而不知道自己在做什么)。
- 以节省便士超过英镑的方式进行优化(无效的优化)。
- 为所有事情寻求绝对的终极效率峰值。
- 在非关键路径中寻求效率。
- 在您几乎无法维护/调试代码时尝试优化。
这些都与您的优化时间无关,而是经验和理解——从理解关键路径到理解实际提供性能的内容。
Knuth 的论文没有涉及诸如测试驱动开发和主要关注界面设计之类的内容。这些是更现代的概念和想法。他主要专注于实施。
尽管如此,这是对 Knuth 建议的一个很好的更新——首先通过测试和界面设计来寻求建立正确性,从而为您留出优化空间而不破坏一切。
如果我们尝试应用对 Knuth 的现代解释,我会在其中添加“ship”。即使您正在通过衡量的收益优化软件的真正关键路径,如果世界上最快的软件永远不会发布,它也是毫无价值的。牢记这一点应该有助于您做出更明智的妥协。
我倾向于多次访问数据库,我
认为是正确的举动。更重要的是我完成
项目,我觉得我因为优化而被挂断了
像这样。我的问题是:这是什么时候使用的正确策略
避免过早优化?
当您最了解自己的要求时,考虑到以上几点,您将做出最佳判断。
我建议的一个关键因素是,如果这是一条处理繁重负载的性能关键路径,那么以一种留有足够优化空间的方式设计您的公共接口。
例如,不要设计一个粒子系统,其客户端依赖于Particle 接口。当您只有封装状态和单个粒子的实现可以使用时,这就没有优化的空间了。在这种情况下,您可能必须对代码库进行级联更改才能进行优化。如果道路只有 10 米长,赛车就无法利用它的速度。而是针对聚合一百万个粒子的ParticleSystem 接口进行设计,例如,在可能的情况下使用更高级别的操作来处理散装粒子。如果您发现需要优化,这将为您提供足够的优化空间,而不会破坏您的设计。
我完美主义的一面想让一切都变得最优,
第一次完美,但我发现这很复杂
设计相当多。
现在这部分听起来有点为时过早。一般来说,你的第一遍应该是简单的。简单性通常与相当快的速度并驾齐驱,即使您正在做一些多余的工作,也比您想象的要快。
无论如何,我希望这些要点至少有助于增加一些考虑因素。