什么时候优化不是过早的,因此不是邪恶的?


75

“过早的优化是万恶之源”,这几乎是我们所有人都听过/读过的。我很好奇什么样的优化不是过早的,即在软件开发的每个阶段(高级设计,详细设计,高级实现,详细实现等),我们可以考虑的优化程度是什么,而不必跨入阴暗面。



Answers:


116

当您基于经验时?不邪恶。“每次我们使用X时,都会遭受性能的残酷打击。让我们计划这次优化或完全避免使用X。”

什么时候相对不痛?不邪恶。“以Foo或Bar来实现这一目标将花费同样多的工作,但从理论上讲,Bar应该效率更高。让我们来吧。”

当您避免笨拙的算法会严重扩展时?不邪恶。“我们的技术负责人说,我们提出的路径选择算法在阶乘时间内运行;我不确定这意味着什么,但她建议我们甚至考虑考虑使用seppuku。让我们考虑其他因素。”

邪恶来自花费大量时间和精力解决实际上不存在的问题。当问题确实存在时,或者当幻影伪问题可以廉价地解决时,邪恶就消失了。


Steve314Matthieu M.在应考虑的评论中提出了观点。基本上,某些类型的“无痛”优化根本不值得,或者因为它们提供的微不足道的性能升级不值得代码混淆,它们正在复制编译器已经在执行的增强功能,或者两者都没有。请参阅注释,以获取一些过于巧妙的非改进示例。


22
有时候,轻松地解决幻影问题仍然是轻度的邪恶,因为这可能导致更难阅读,更难以维护代码。难度不大(或者这不是一个简单的解决方案),但有时仍然有意义。一个例子可能是使用了一些人不会意识到的聪明的按位技巧,并且如果有用的话,编译器也可能会应用。
2011年

26
我在这里同意史蒂夫的观点,有时“优化”根本不值得,尤其是因为编译器是如此之好。例子?如果i未签名,i / 2则可以替换为i >> 1。它更快。但这也更加神秘(并不是每个人都会看到效果,即使是这样做的人也可能会浪费时间)。但是最糟糕的是编译器仍然会这样做,所以为什么要混淆源代码;)?
Matthieu M.

19
@Larry:我没有,所以我想这是一个很好的例子。
Joris Meys 2011年

18
在我看来,即使是简单的优化也会影响代码的可读性/可维护性,不是基于实际的性能指标,因此也应视为有害的。
Bart van Ingen Schenau 2011年

14
@Matthew:教他们什么?肮脏和不必要的把戏?为什么?如果剖析显示a i/2确实是一个热点,并且(确实令人难以置信,但让我们假设)i>>1使a更快,那就去做,然后对它进行评论,以表明此剖析表明它更快。如果确实在任何地方都需要这样做(我对此表示怀疑,因为正如Matthieu所说,编译器应该足够聪明才能自己执行此操作),那么新手将学到一些东西,如果不需要(可能),为什么要插入他们的头上有不必要的民间传说?
2011年

38

应用程序代码仅应尽可能好,但库代码应尽可能好,因为您永远不知道如何使用库。因此,当您编写库代码时,它在各个方面都必须是好的,无论是性能,健壮性还是任何其他类别。

另外,在设计应用程序选择算法时,还需要考虑性能。如果不是为了实现高性能而设计的,那么以后没有任何程度的黑客可以使它具有高性能,并且任何微优化都不会超过高级算法。


5
库代码应该记录它是否试图“尽可能地好”,或其目的是什么。只要有用的代码只有消费者适当使用,代码就不必绝对最佳。
超级猫

1
抱歉,但“在所有方面都做到最好”听起来像是工程过度。另外,这可能不现实-生活总是要权衡。
sleske 2015年

1
+1用于强调设计阶段;如果您故意权衡其好处,那还为时过早。
内森·塔吉

相反,如果您永远不知道将如何使用库,就不会知道花时间进行改进是否具有任何业务价值。因此,这几乎不是一个论点。
RemcoGerlich


17

什么时候优化不是过早的,因此不是邪恶的?

善与恶是很难说的。谁拥有这项权利?如果我们看自然,似乎我们已经为生存编程了“生存”的广义定义,其中包括将我们的基因传给后代。

因此,至少根据我们的基本功能和编程,我想说的是,优化与复制目标保持一致时,它并不是邪恶的。对于男生,有金发,黑发,红发,很多可爱。对于女孩来说,有男人,其中一些似乎还可以。

也许我们应该为此目的进行优化,在那里使用分析器会有所帮助。探查器将为您提供有关热点及其发生原因的详细信息,从而使您可以更有效地对优化进行优先级划分和时间安排。这将使您有更多的空闲时间用于复制及其追求。


3
看到有人给这个老栗子带来新鲜​​的感觉,这令人耳目一新。只需要阅读Knuth的全部引文,而不仅仅是一句话,是吗?
罗伯特·哈维

1
@RobertHarvey我在那儿有些烦恼-因为如此多的人似乎只引用了这句话,而且很多重要的上下文信息最终在此过程中丢失了。我不确定这是一个好答案,因为我得到了一点保证。:-D

14

全报价定义优化时不早衰:

一个好的程序员不会因这种推理而沾沾自喜,他应该明智地仔细看待关键代码。但是只有在确定了该代码之后。[强调我的]

您可以通过多种方式识别关键代码:关键数据结构或算法(例如,使用频繁的项目或项目的“核心”)可以进行重大优化,可以通过探查器识别许多次要优化,等等。


6
是的...将随机函数调用的时间减少90%很好,但是通过查看代码,您的应用实际上花费了80%的时间并节省了一个时间,可能会产生更大的影响百分之几。

11

在所有情况下,您都应该根据自己的经验选择一个“足够好”的解决方案。

最优化的说法是在实际知道有必要之前编写“比'足够好'的代码复杂得多的代码,以使其更快”,因此使代码比必需的更加复杂。复杂性使事情变得困难,所以这不是一件好事。

这意味着您不应该选择一个超级复杂的“可以通过透明交换到磁盘来分类100 Gb文件的磁盘”排序例程,但是简单的排序应该是一个很好的选择。盲目选择Bubble Sort或“随机选择所有条目,并查看它们是否顺序一致。重复。” 很少好。


3

我的一般经验法则:如果您不确定是否需要优化,请假设您不需要。但请记住,何时需要进行优化。但是,您可能需要预先了解一些问题。这通常涉及选择好的算法和数据结构。例如,如果您需要检查集合中的成员资格,则可以肯定地确定您将需要某种类型的集合数据结构。


3

以我的经验,在详细的实施阶段,答案在于对代码进行概要分析。重要的是要知道什么需要更快,什么可以被接受。

了解确切的性能瓶颈在哪里也很重要-优化代码的一部分仅花费总运行时间的5%就不会有任何好处。

步骤2和3描述了非过早的优化:

  1. 让它起作用
  2. 测试。不够快?剖析它
  3. 使用步骤2中的数据,优化代码中最慢的部分。

您忘记了步骤0,即:正确地构建应用程序,以便从一开始就可以期望合理的性能。
罗伯特·哈维

我只是在谈论详细的实施阶段。
Gorgi Kosev 2011年

1
我对第3步提出疑问-很多时候,最好的答案是找出不同的方法,这样一来您就不必做慢的代码了。
罗伦·佩希特尔

1
选择正确的数据结构。
杰森克2012年

3

当选择难以更改的东西(例如硬件平台)时,这不是优化。

选择数据结构是一个很好的例子-对于满足功能和非功能(性能)要求都至关重要。不容易更改,但是它将驱动您应用程序中的其他所有功能。您的数据结构会更改可用的算法等。


3

我只知道回答这个问题的一种方法,那就是获得性能调整方面的经验。这意味着-编写程序,然后编写程序之后,在其中找到加速并进行迭代。这是一个例子。

这是大多数人犯的错误:他们尝试实际运行程序之前对其进行优化。如果他们上了一门编程课程(实际上是没有多少实践经验的教授),他们将戴上大O型彩色眼镜,他们会以为这就是全部。都是相同的问题,需要事先进行优化。**

有人说:首先要正确,然后再快速。他们是对的。

但是现在要考虑的是:如果您做了几次,您就会意识到您之前所做的愚蠢的事情会导致速度问题,因此您本能地避免了它们。(诸如使您的类结构变得过于沉重,被通知淹没,使函数调用的大小与它们的时间开销相混淆的事情,该列表会不断出现……)您本能地避免了这些,但请猜测一下这看起来是什么-经验丰富:过早的优化!

因此,这些愚蠢的辩论一直在继续:)

**他们说的另一件事是,您不必再为它担心,因为当今的编译器非常好,并且计算机非常快。(KIWI-用Iron杀死它。)没有指数级的硬件或系统加速(由非常聪明的勤奋的工程师完成)可以补偿指数级的软件速度下降(由以这种方式思考的程序员完成)。


2

当需求或市场明确要求时。

例如,在大多数金融应用中,性能是必不可少的,因为低延迟至关重要。根据交易工具的性质,优化可以从使用高级语言的非锁定算法到使用低级语言甚至是极端的方法进行优化-在硬件本身中实现顺序匹配算法(例如,使用FPGA) )。

其他示例是某些类型的嵌入式设备。以ABS制动器为例;首先是安全性,当您撞车时,汽车应该减速。但是也有性能,您不希望遇到任何延迟。


0

如果您要优化的某些性能不会导致系统的“软故障”(虽然有效,但仍然没有用),那么大多数人会认为优化还为时过早。

真实的例子。

  • 如果我的冒泡排序需要20毫秒才能运行,那么将其优化为1毫秒即可实现快速排序,尽管性能提高了2000%,但不会以任何有意义的方式增强整体实用性。

  • 如果网页加载需要20秒,而我们将其减少到1秒,则可以将网站的效用从0提高到无限。基本上因为太慢而损坏的东西现在很有用。


重要的是要注意,如果在程序执行过程中您的排序被调用了1000次,那么从20ms到1ms的优化是很重要的。
Beefster

0

什么样的优化还不成熟?

一种用于解决应用程序中已知性能问题的优化,或者一种用于使应用程序满足定义良好的接受标准的优化。

确定后,应该花一些时间来确定此修复程序并评估性能收益。

(即不是-“我认为这段代码看起来可能很慢,我将X改为使用Y,这样会更快”)。

我遇到了很多过早的“优化”,这最终使代码变慢了–在这种情况下,我认为过早的意思是“没有仔细考虑”。性能应该在优化之前和之后进行基准测试,并且仅保留实际可提高性能的代码。


0

“过早的优化是万恶之源”是我们几乎所有人都读/读过的东西

真正。不幸的是,它也是有史以来最(恶意)滥用编程引用之一。由于Donald Knuth创造了模因,因此有必要在报价中添加一些原始内容:

我们应该忘记效率低下的问题,例如大约97%的时间:过早的优化是万恶之源。但是,我们不应该放弃那临界的3%的机会。……优秀的程序员……明智地仔细看待关键代码;但只有在识别出该代码之后。...一直使用测量工具的程序员的普遍经验是,他们的直觉猜测会失败

注意,Knuth特别讨论了运行时的执行速度

..程序员浪费大量时间来思考或担心程序非关键部分的速度

另外,他在1974年写了这篇文章,当时任何机器资源在执行速度和程序的可维护性(更高的速度-更少可维护性)之间处于溢价和负相关关系时,都可能比现在更强大。

好的,要回答您的问题,根据Donald Knuth的说法,如果优化能够解决已发现的严重性能瓶颈(在性能分析过程中进行理想地测量和确定),则优化还为时过早。

正如我之前所说,“过早优化”是最恶意滥用的模因之一,因此,如果没有一些不是过早优化但有时会被忽略的事情的例子,答案将是不完整的:

  • 肉眼可见的瓶颈,在被引入之前可以避免,例如O(N ^ 2)到具有大N的数据库的往返次数,其中存在O(1)替代

甚至与运行时执行的速度都没有关系:

  • 周到的前期设计

  • 静态输入(!)

  • 等/任何形式的精神努力

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.