如何处理关于“过早优化是万恶之源”的误解?


26

我遇到了很多人,他们在原则上反对任何在英语中通常被认为是“优化”的东西,他们经常逐字引用(部分)引用“过早的优化是万恶之源”。作为他们立场的理由,暗示他们将我所说的一切解释为“过早的优化”。但是,这些视图有时是如此可笑地根深蒂固,以至于它们几乎消除了任何与最纯粹的“天真的”实现方式有关的算法或数据结构偏差……或者至少与它们之前的工作方式无关。在听到“性能”或“优化”的消息后,人们如何才能像这样使人们再次“张开耳朵”?我该如何讨论对性能有影响的设计/实现主题,而又不会让人立即想到:“这个人想花两周时间来编写十行代码?”

现在,在这里以及在Web的其他角落已经讨论 “是否所有优化都是过早的并因此有害的”的立场,并且已经讨论了如何识别优化何时过早并因此有害的立场,但是不幸的是,现实世界中仍有一些人不愿意挑战自己对反优化的信念。

以前的尝试

几次,我试图提供Donald Knuth的完整报价,以解释“过早的优化是不好的”↛“所有的优化都是不好的”:

我们应该忘记效率低下的问题,例如大约97%的时间:过早的优化是万恶之源。然而,我们不应放弃我们那临界的3%的机会。

但是,在提供完整报价时,这些人有时有时会变得更加确信我在做的是Premature Optimization™,并深入研究并拒绝听。好像“优化”一词使他们感到害怕:在某些情况下,我能够通过避免使用“优化(e | ation)”一词来提出实际的性能改进代码更改,而不会被否决。和“性能”(这个词也很吓人),而是使用“替代架构”或“改进的实现”之类的表达方式。因此,这似乎真的是教条主义,而不是他们实际上是对我的批评进行评估,然后视其为不必要和/或过于昂贵而将其驳回。


10
那么,你有这样的讨论最后一次,你真的测量其性能将是坏由最纯净,天真的实施?或者,至少对预期的运行时间进行了粗略的估算?如果没有,那其他人本可以完全正确地表达自己的观点,那么您就无法知道。
布朗

12
@errantlinguist:如果该程序确实“像糖蜜一样慢”,那么显然您应该能够轻松地检测到Knuth的“临界3%”,因此胜过反对优化它的任何论点。而且,如果您无法检测到该信息,那么您还没有完成家庭作业,还没有准备好进行优化。因此,目前尚不清楚问题出在哪里。
Nicol Bolas

6
@errantlinguist:如果您提供的证据表明该部分代码对于应用程序而言是一个严重的性能问题,并且整个应用程序比所需的速度慢,而他们仍然否认需要修改代码,那么它不会没关系。您正在与无法接受基于证据的推理的人打交道,因此是不合理的。
Nicol Bolas

6
@errantlinguist:关键问题:客户是否抱怨该领域的应用程序运行缓慢?
Gort Robot

5
我之所以投票结束,是因为OP显然只是在寻找可以证实观点的人,而不是对实际问题的答案。我认为这也不会(或不应)在Workplace.SE上保持打开状态。
BlueRaja-Danny Pflughoeft

Answers:


35

看来您正在寻找捷径,而不是先尝试“最纯真的实现”,而直接实现“更复杂的解决方案,因为您事先知道纯朴的实现不会这样做”。不幸的是,这很少能奏效—当您没有确凿的事实或技术论据来证明天真的实现太慢或太慢时,您很可能错了,而您正在做的过早的优化。试图与Knuth争论与事实不符。

以我的经验,您要么不得不硬着头皮先尝试“天真的实现”(并且可能会惊讶地发现这样做足够快),或者至少会对运行时间做出粗略的估计,例如:

“天真的实现将是O(n³),并且n大于100.000;它将运行几天,而天真的实现将在O(n)中运行,这只需要几分钟。”

只有掌握了此类论据,您才能确定优化还不成熟。

恕我直言,只有一个例外:当更快的解决方案也是更简单,更干净的解决方案时,则应从一开始就使用更快的解决方案。标准示例是使用字典而不是列表以避免不必要的循环代码进行查找的方法,或者使用良好的SQL查询来提供恰好您需要的一个结果记录,而不是必须使用较大的结果集。之后在代码中过滤。如果遇到这种情况,请不要争论绩效-表演可能会带来额外的好处,但最有可能是无关紧要的,当您提到它时,人们可能会倾向于使用Knuth来对付您。关于可读性,较短的代码,更简洁的代码,可维护性的争论–这里不需要“掩盖”任何东西,而是因为这里的(和仅那些)是正确的参数。

以我的经验,后一种情况很少见-更典型的情况是,首先可以实现一种简单,幼稚的解决方案,与更复杂但可能更快的解决方案相比,这种解决方案更易于理解且不易出错。

当然,您应该充分了解需求和用例,以了解可接受的性能,以及在用户眼中什么时候变得“太慢”。在理想的情况下,您会得到客户的正式性能规格,但是在现实世界的项目中,所需的性能通常是一个灰色区域,只有当用户注意到程序在生产中表现为“太慢”时,用户才会告诉您。通常,这是找出某件事太慢的唯一方法,即用户反馈,然后您无需引用Knuth就能说服您的队友“天真的实现”还不够。


15
@errantlinguist:也许我没有说清楚,还是根本不想听?我的建议是:。用*不要试图“关于克努特的‘3%’或‘97%’的哲学论点保持它的事实,否则你的同事最可能是正确的,你的性能参数是不合适的
布朗博士

4
@errantlinguist:在您对卡尔·比勒费尔德的回答的评论中描述的情况下,您似乎可以支持所有论点,而无需使用“表现”。我会更进一步地说,如果您在这种情况下争论绩效,您会犯下一个巨大的错误,因为您的同事是对的:绩效通常并不重要。争论简单性,可读性,可维护性,更少的代码行,而不是关于性能的(!),甚至不作为旁注。不要向他们提供对您使用Knuth的可能性。
Doc Brown

2
@errantlinguist:不要埋葬 -当正确地关注那些方面时,应将这些方面放在焦点上,并且当您无法证明对最终用户有重要影响时,不要将性能作为论据。
Doc Brown

2
@errantlinguist:我不确定你如何得出这个结论。布朗博士的答案似乎很清楚:通过坚持关于什么是可接受的绩效和不可接受的绩效的事实陈述,消除了与同事之间产生的这些无用的论点。
jl6

1
对于小型编程人员来说,这是一个很好的建议,但是在架构设计层面忽略性能问题可能会使团队陷入漫长的僵局,因为在被迫面对问题之前,它可能已经做了大量工作,并且不能保证当问题是体系结构时,大部分工作是可重用的(我已经看到它杀死了产品。)我知道您的回答中有一个例外,但是要知道它是否适用,您仍然必须提出问题,甚至问这个问题显然对errantlinguist的同事都是反感。
sdenham '16

18

问问自己:

  • 该软件是否不符合性能规格?
  • 该软件是否存在性能问题?

这些是要优化的原因。因此,如果有人反对,只需向他们展示规格并返回给他们,并解释说我们需要优化,因为我们不符合规格。除此之外,一个人很难说服其他人优化是必要的。

我认为报价的重点是,如果您没有问题,请不要执行不必要的优化,因为时间和精力可能会花在其他地方。从业务前景来看,这是完全合理的。

第二,对于那些担心优化的人,总是用指标来备份性能结果。代码快多少?与以前相比,性能提高了多少?如果一个人只花了两周时间就将代码比以前的版本提高了2%,那么如果我是你的老板,我将不会高兴。那两个星期本可以花在实施一项新功能上,以吸引更多的客户并赚更多的钱。

最后,大多数软件不必进行高度优化。只有在少数几个专业行业中,速度才真正重要。因此,大多数情况下,可以使用预先存在的库和框架来取得良好的效果。


4
尽管提供了很好的信息,但实际上并不能回答我的问题,即如何与那些与绩效息息相关的人进行深入讨论。
errantlinguist

7
除了“只有在某些专业行业中速度才是真正重要的”,我都同意所有这些观点。我认为您从客户的角度低估了存在性能问题的软件数量。
Gort Robot

@StevenBurnap:是的—在野外有没有真正慢的Web应用程序?
errantlinguist '16

1
google.com非常快。:-P
Gort the Robot

尝试在EDGE移动连接上使用google.com。是的,这是一个荒谬的极端情况,但是肯定不会很快。;)(实际上不是故意的双关语-确实)
errantlinguist

7

在讨论与绩效息息相关的那一刻如何与之打交道?

从建立在团队战略方向上的共同原则开始。

我的原则:

我写代码的个人原则是,首先要确保程序的正确性,然后对其进行概要分析并确定是否需要优化。我自己分析我的代码,因为其他程序员是我代码的潜在使用者-如果速度太慢,他们将不会使用它-因此,对于我的代码而言,速度是一项功能。

如果您的消费者是客户,那么您的客户会告诉您是否需要更快的代码。

但是,人们可以在代码中找到已知的,明显更好的选择。由于以下几个原因,我宁愿在第一稿中做到正确:

  1. 如果我第一次做对了,那么我可以忘记实现(利用信息隐藏的优势),而且我不会用TODO弄乱我的代码。
  2. 其他人(尤其是那些只在工作中学习的人)认为它做得正确,并且他们会学习并使用相同的代码风格。相反,如果他们看到我做错了方法,他们也会做错了方法。

假设优化需求是正确的

假设这是需要优化的代码中真正重要的部分,您可以将Schlemiel的寓言告诉Painter,或者强调引号的其余部分:

“程序员浪费大量时间来思考或担心程序非关键部分的速度,而在考虑调试和维护时,这些对效率的尝试实际上会产生严重的负面影响。我们应该忘记效率低下, 97%的时间:过早的优化是万恶之源,但我们不应该在这3%的临界风险中放弃机会。” -唐纳德​​·努斯

权衡额外复杂性的成本

有时,就增加的复杂性的可维护性而言,确实要付出一定的代价。在这种情况下,您可以将辅助实现保留在不同的函数或子类中,并对其应用相同的单元测试,以便毫无疑问它是正确的。以后,如果您对代码进行概要分析并发现简单的实现会成为瓶颈,则可以切换优化的代码并明显改善程序。

领导

有时候问题出在自我上-有些人宁愿使用次优或错误的代码,也不希望别人比自己更正确。您可能想避免与这些人一起工作。

领导力,尤其是当您没有对人的职位权限时,是关于提出合理的建议并指导他人达成共识。如果您不能引导您的团队进行讨论,那么此事就不值得紧迫地解决。可能有更大的鱼要炸。


6

前进的道路是忘记实际的报价和各种解释-教条主义要么集中于一位大师的特定报价,要么就任教。谁说Knuth总是对的?

而是专注于手头的项目,您正在开发的软件以及您不同意的同事。对于此软件可接受的性能有什么要求?比这慢吗?然后优化。

您不必将其称为“优化”,而可以将其称为“修复错误”,因为根据定义,如果实现不符合要求,它就是错误。

更一般而言,有两种关于优化的可能性:

  1. 优化的代码也更短,更易于理解和维护。

  2. 优化的代码更难理解,花费更长的时间编写和测试,或者如果需求以意想不到的方式改变,将来的更改也将更加复杂。

如果情况是(1),则您甚至不必争论优化问题。但是,如果(2)是这种情况,那么您正在权衡决策。这实际上是业务级别的决定,而不仅仅是技术决定。您必须权衡优化成本与收益。为了获得更大的收益,效率低下首先要成为问题,要么是糟糕的用户体验,要么是硬件或其他资源的成本显着增加。


好吧,我完全同意你的第一句话。但是,我很确定某个软件在没有用正式方式明确指定性能要求的情况下,“对于预期的用例而言会非常缓慢”。
布朗

@DocBrown:当然,但是无论如何,都是由客户来决定是否太慢,而不是开发者。
JacquesB 2016年

我从未遇到过明确说明性能要求的业务要求。
errantlinguist

@errantlinguist:以我的经验,这在以客户为中心的企业(例如在线商店)中很常见。但是对于公司内部使用的工具和应用程序,项目所有者通常不会考虑用户体验。
JacquesB '16

4

我认为上下文中的完整引用是有启发性的。我将从我在Reddit上发表的有关该主题的帖子中复制:

毫无疑问,效率的圣杯会导致滥用。程序员浪费大量时间来考虑或担心程序非关键部分的速度,而在考虑调试和维护时,这些提高效率的尝试实际上会产生严重的负面影响。我们应该忘记效率低下的问题,例如大约97%的时间:过早的优化是万恶之源。

然而,我们不应放弃我们那临界的3%的机会。优秀的程序员不会因这种推理而沾沾自喜,他应该明智地仔细看待关键代码。但只有在识别出该代码之后。

-Donald Knuth,“ 转到语句的结构化编程” ACM计算调查,1974年12月,第6卷,第4期,第268页

重点是,与过早地关注优化相比,还有更多重要的事情要担心。当然,您应该仔细考虑数据结构和算法(在3%之内),但不必担心减法是否比模数更快(在97%之内),直到可以清楚地看到低级优化是必要。

前者不一定在您的同事正在思考的意义上进行优化,而是在选择错误的算法和数据结构不是最佳的意义上进行优化。


2
可能有人会补充说,Knuth显然不认为分析算法的时间复杂度并在此基础上做出设计选择是过早的优化。
sdenham '16

3

以我的经验,如果您经常遇到这种对优化的反对,那么人们就不会真正抱怨优化。他们抱怨您以优化的名义牺牲了什么。这通常是可读性,可维护性或及时性。如果您的代码在相同的时间内交付并且很容易理解,那么如果您使用的是更高效的数据结构和算法,人们将不会在乎。在这种情况下,我的建议是努力使您的代码更加优雅和可维护。

如果您在反对他人代码方面遇到这种反对,通常是因为您建议进行大量返工。在这些情况下,您确实确实需要一些实际的测量来证明这是值得的,或者可能在编写代码之前尝试尽早参与设计阶段。换句话说,您需要证明它在3%之内。如果我们重写了不完全符合我们要求的所有代码,我们将永远无法完成任何工作。


不幸的是,我实际上做了相反的情况,例如Deque在Java标准库中使用a 来替换ArrayList在处理代码时围绕作为堆栈使用的大量逻辑……这被标记为更改评论。换句话说,审阅者希望拥有更多的代码,因为他不熟悉,因此代码也更慢且更容易出现错误Deque
errantlinguist 2016-4-12

3
不愿意学习已经用您的语言学习了10年的东西,这对程序员是一种有害的态度,也是比您最初描述的要深得多的问题。就个人而言,在这种情况下,我会拒绝该建议,并在需要时将其上报给管理层。
Karl Bielefeldt

5
@errantlinguist:当您的审阅者提出一种明显较差的(意味着更复杂的)解决方案来代替干净简单的解决方案时,您应该对此进行争论。不要争论性能!认真地说,在该讨论中甚至不要使用“性能”一词。仅争论可读性和可维护性。如果审稿人坚持自己的错误代码,请升级。
布朗

+1不确定为什么这个答案会被否决而不是被认可。它提出了一种解决问题的方法,还提出了对真正潜在问题的分析(即,没人希望被告知必须彻底重写其代码)。
Andres F.

2

关于此报价确实存在很多误解,所以最好退后一步,看看实际的问题是什么。问题不在于您永远不应该“优化”。这就是“优化”绝不是您应该着手完成的任务。您永远不要在早晨醒来,对自己说:“嘿,我今天应该优化代码!”。

这导致浪费精力。只是看代码并说“我可以使其更快!” 导致付出大量努力使某些事情变得更快,而这些事情首先就足够快了。您可能会感到自豪,因为告诉自己您编写代码的速度提高了四倍,但是如果该代码是在按下按钮时进行的计算,并且在向人类用户显示之前首先花费了10毫秒的时间,该死的

那就是“过早优化”中的“过早”。什么时候不是“过早的”?当客户告诉您“这太慢了,请解决它!” 那是您挖掘代码并尝试使其更快的时候。

这并不意味着您应该关闭大脑。这并不意味着您应该在单个链接列表中保留10,000个客户记录。您应该始终了解自己所做的工作对性能的影响,并采取相应的措施。但您的想法是,您不必故意花额外的时间来尝试使其更快。您只是从其他选择中选择性能更高的选择。


这并不意味着您应该关闭大脑。这并不意味着您应该在单个链接列表中保留10,000个客户记录。>尽管我100%同意您的意见,但实际上我已经看到过以这种方式使用的链表,并被告知“不要碰它”。
errantlinguist

尽管提供了很好的信息,但实际上并不能回答我的问题,即如何与那些与绩效息息相关的人进行深入讨论。
errantlinguist

3
可悲的是,“单链列表”并不是一个随机的例子,而是我个人遇到的一个问题。
Gort Robot

1

您可以以错误的方式进行操作,也可以以正确的方式进行操作。

通常,事情以错误的方式进行,代码被重构,从而以正确的方式进行。如果您要编写新代码,并且知道可以正确地做事而不会受到重大损失,那么我会错误地以正确的方式做事。(请注意,在性能测试等之后,可能需要进行一些更改-没关系。或者,完全天真的实现很少(如果有的话)是对的。)

如果您a)知道将来会有所帮助,或者b)知道次优路径会导致问题,那并不一定是过早的优化。真的,这就像下棋游戏。

我认为人们倾向于做正确的事情,而不是做错事情。与同事讨论替代策略时,请使用此选项。


5
从来没有“错误的方式”或“正确的方式”。从“我的上帝,这甚至如何运行!?”开始,通常会有无数种方法连续运行。“约翰·卡马克(John Carmack)和唐纳德·克努斯(Donald Knuth)不能在结对编程时做到更好”。
Gort Robot

@StevenBurnap这是事实。但是,我认为对于个人来说,通常有一些正确的方法和许多错误的方法。(随着我们成为更好的程序员,这一领域开始发生变化-我们过去的“正确方法”有时可能会成为我们的新“错误方法”,而我们的新正确方法要比旧方法更好。)我认为在最适合您的最佳方式。这导致我们成为更好的程序员,成为更好的团队成员(指导很重要!)和编写更好的代码。
Lunchmeat317 '16

我认为,人们往往会希望做正确的事情,而不是做他们错了 ” -但问题是,有非常关于什么是对错不同的意见。有些人甚至对此发动了圣战(从字面上看)。
JensG '16

1

这似乎是通信问题,而不是编程问题。尝试理解人们为什么会感觉自己的方式,并试图明确为什么您认为自己的方式会更好。完成此操作后,请勿提出对抗性争论,而争论的目的是告诉别人他们为什么错了而你正确了。说明您的想法和感受,并让人们对此有所反应。如果您无法达成任何共识,并且您认为这是一个非常关键的问题,那么整个团队可能就存在一些严重的问题。

更专注于实际编程,不要将时间浪费在长篇大论的争论上,而只是觉得自己“更快”。如果您看到有人编写一个在Web应用程序中每个请求调用一次的方法,并且当您知道它确实是O(log(n))时间问题时,它的时间复杂度为O(n ^ 2),那么可以肯定,如果这样的话毫无疑问,继续吧。

但是请注意,作为人类,我们的程序员在猜测应用程序的哪些部分会遇到瓶颈方面确实很糟糕(我的意思是AWFUL)。埃里克·利珀特(Eric Lippert)在博客文章中撰写了有关这个有趣主题的文章。始终主张可维护性。当您有更多信息时,可以轻松地(相对地)解决最终发现的任何性能问题。


我编辑了答案,并充实了一些内容,下降投票者可以添加一些反馈吗?:)
sara

尽管我不是拒绝投票的人,但是您的第一段是解决当前问题的重点,但其余部分实际上并没有回答我的问题,即如何与那些与绩效息息相关的人进行深入讨论(尽管它仍然是很好的建议)。
errantlinguist 2016年

基本上,我希望在最后两段中提到的是“那些优化可能甚至没有关系”。在这种情况下,最好选择打架。
萨拉
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.