何时优化过早?

6

我经常看到人们使用这个术语,但我觉得大多数人使用它是因为懒惰或无知。例如,我正在阅读这篇文章:

http://blogs.msdn.com/b/ricom/archive/2006/09/07/745085.aspx

他在谈论他为应用程序实现必要类型所做的决策。

如果是我,谈论我们需要编写的代码,其他程序员可能会认为:

  1. 我过于提前考虑,因此过早优化。
  2. 在没有遇到任何减速或性能问题时,过度思考不重要的细节。

或者两者都有。

并且会建议只实现它,不要担心这些问题,直到它们成为问题。

哪种更可取?

如何在进行任何实现之前区分对性能关键应用程序的过早优化与知情决策制定?


在每种情况下都不同,但是从一开始就设计良好的架构将使得在未来需要进行优化时更容易实现。 - stephen776
请查看这个答案 - Mike Dunlavey
2
以下是一些你可能会感兴趣的其他文章:过早优化的谬论“过早优化是邪恶的”神话 - LukeH
10个回答

13

如果以下情况发生,优化就太早了:

  1. 你的应用程序没有任何时间紧迫的任务。这意味着,如果你正在编写一个程序,在文件中添加500个数字,单词“优化”甚至不应该出现在你的脑海中,因为它只会浪费你的时间。

  2. 除非你正在使用汇编语言处理时间关键的任务,否则还在担心 i++; i++; 是否比 i += 2 快... 如果真的是那么关键,你将会使用汇编语言工作而不浪费时间担心这个。(即使是这样,这个特定的示例也很可能无关紧要。)

  3. 你有一种直觉,认为一件事情可能比另一件事情快一些,但你需要查看资料。例如,如果你对于 StopWatchEnvironment.TickCount 哪个更快有疑问,那就是过早优化,因为如果差距更大,你可能会更确定,并且不需要查看资料。

如果你猜想某些地方可能会很慢,但你不太确定,只需添加一个 //NOTE: Performance? 的注释,如果以后遇到瓶颈,请检查代码中这些地方。我个人不担心那些不太明显的优化;如果需要,我只是稍后使用分析器。

另一种技术:

我只需运行程序,在随机位置断点调试,看看它停在哪里——无论它停在哪里都可能是瓶颈,而且停在那里的次数越多,瓶颈就越严重。它几乎可以像魔术一样起作用。 :)


4
随着现代编译器的水平,你需要非常熟练于汇编语言(更不用说在理解CPU、总线、内存等架构方面成为一个专家)才能超越它们。 - Eldad Mor
1
++ 你最后一段所提到的方法是我依赖的。由于它引起了很多怀疑,我尝试用大量的统计论据进行了解释,并在此链接中分享:https://dev59.com/xHI-5IYBdhLWcg3wlpeW#1779343 和 https://dev59.com/3G855IYBdhLWcg3wXS8I#4390868。 - Mike Dunlavey
1
在你的最后一段加上一个大加号1,那是我最喜欢的调试技巧之一;-) - Chris O
@George Hawkins - 当然。我特别指的是使用汇编进行优化。如果您不是汇编语言、操作系统和所写硬件的专家,那么很难超越编译器。 - Eldad Mor
@George:我认为手写汇编代码能够击败编译代码还有另一个原因。这不是因为编译器的任何弱点,而是因为汇编语言写起来非常麻烦,所以你只会写必要的部分。 - Mike Dunlavey
显示剩余4条评论

4
这句谚语(我认为)并不是指优化在设计时就已经内置了。它指的是专门针对性能进行的任务,否则就不会被执行。
根据常规智慧,这种优化不会“变得”过早,而是有罪推定,除非证明无罪。

4

优化是使现有的代码更加高效(更快速、资源使用更少)的过程。

所有优化都是不成熟的,如果程序员没有证明它是必要的。(例如,通过运行代码来确定它是否在可接受的时间内实现了正确的结果。这可以简单地运行它以“看”它是否运行得足够快,或者在性能分析器下进行更仔细的分析)。

编写良好的程序有几个阶段:

1)设计解决方案并选择一个好的高效算法

2)以可维护的方式实现解决方案。

3)测试解决方案,并查看它是否满足您对速度、RAM使用等的要求。(例如,“当用户点击“保存”时,需要少于1秒吗?” 如果需要 0.3 秒,那么您真的不需要花一周的时间来优化它,使时间降到 0.2 秒)

4)IF 它不符合要求,则考虑原因。在大多数情况下,这意味着转到第(1)步,现在您更好地了解了问题,以找到更好的算法。(编写一个快速的原型通常是探索这个过程的一种廉价方法)

5)IF 它仍然不符合要求,则开始考虑可能有助于加速运行时的优化(例如查找表、缓存等)。为了推动这个过程,性能分析通常是一个重要的工具,帮助您定位代码中的瓶颈和低效之处,以便您可以在编码上花费的时间中获得最大的收益。

我应该指出,一个经验丰富的程序员在处理一个相对熟悉的问题时,可能能够在头脑中跳过第一步,然后只是应用模式,而不是每次都亲自完成这个过程,但这只是通过经验获得的一种捷径。

因此,有许多“优化”是经验丰富的程序员会自动构建到他们的代码中。这些不是“过早优化”,而是“常识效率模式”。这些模式很快、易于实现,但极大地提高了代码的效率,您无需进行任何特殊的定时测试来确定它们是否有益:

  • 不要在循环中添加不必要的代码。(类似于从现有循环中删除不必要的代码的优化,但不涉及编写两次代码!)
  • 将中间结果存储在变量中,而不是一遍又一遍地重新计算。
  • 使用查找表提供预先计算的值,而不是即时计算它们。
  • 使用适当大小的数据结构(例如,在字节(8位)中存储百分比而不是长整型(64位)将使用少8倍的RAM)
  • 使用预绘制图像绘制复杂窗口背景,而不是绘制许多单独的组件
  • 对打算通过低速连接发送的数据包应用压缩以最小化带宽使用。
  • 以允许使用高质量和良好压缩格式的样式为您的网页绘制图像。
  • 当然,虽然这不是“优化”,但首先选择正确的算法!

例如,我刚刚替换了我们项目中的旧代码。我的新代码没有任何“优化”,但是(与原始实现不同),它是以效率为前提编写的。结果:我的代码运行速度快了25倍 - 只是因为不浪费。我可以优化它使它更快吗?是的,我可以轻松地获得另外2倍的速度提升。我会优化我的代码使它更快吗?不会 - 5倍的速度提升已经足够了,而且我已经实现了25倍的速度提升。此时进一步的工作只会浪费宝贵的编程时间。(但如果需求变化,我可以在未来重新审视代码)

最后,最后一个要点:您所工作的领域决定了您必须达到的标准。如果您正在为游戏编写图形引擎或实时嵌入式控制器编写代码,则可能需要进行大量优化。如果您正在编写像记事本这样的桌面应用程序,则只要不过度浪费,就可能永远不需要优化任何内容。


谢谢,顺便说一下,我修正了一些错别字,希望你不介意。 - Joan Venge
1
@Joan Venge:别担心——我总是在这个脆弱的笔记本键盘上漏打字母 :-) - Jason Williams

3
过早的优化是在你知道需要进行权衡之前为了提高性能而优化代码中一些其他积极属性(例如可读性)的行为。通常,在开发过程中,没有使用任何分析工具来找到代码中的瓶颈就会进行过早的优化。在许多情况下,优化会使代码更难维护,有时也会增加开发时间和软件成本。更糟糕的是,有些过早的优化事实上并不会使代码变得更快,有些情况下甚至会比以前更慢。

有时候,即使你不一定需要它,你也应该进行“优化”:例如,我会说,你永远不应该使用 ArrayList 来代替 List<int>,即使在你特定的程序中没有太大的区别。(但是,如果你想知道,我不是那个给了 -1 的人。) - user541686
@Mehrdad:这更多是一个可维护性问题,而不是优化问题。 - Powerlord
@R.Bemrose:确实是两者兼顾——避免装箱/拆箱显然是一种优化,对我来说这比可读性/可维护性更重要。 - user541686
我认为类型安全和性能同等重要——尽管ArrayList<Integer>是类型安全的,但我仍然经常避免在Java中使用它(我使用其他使用int[]的类代替)。 - user541686

3

刚开始时,交付产品比优化更重要。

随着时间的推移,您将会分析各种应用程序,并学习编码技巧,这些技巧自然会导致代码的优化。基本上,在某个时候,您将能够发现潜在的问题,并相应地构建事物。

但是,在找到实际问题之前不要过于担心。


1

当你编程经验少于10年时。


1

拥有(大量)经验可能是一个陷阱。我认识很多非常有经验的程序员(C\C ++,汇编),他们倾向于过度担心,因为他们习惯于担心时钟周期和多余的位。

在嵌入式或实时系统等领域,这些确实很重要,但在常规的OLTP / LOB应用程序中,大部分工作应该集中在可维护性,可读性和可变性上。


1

优化是棘手的。考虑以下例子:

  1. 决定实现两个服务器,每个服务器都执行自己的任务,而不是实现一个单一的服务器来完成两个任务。
  2. 出于性能原因,决定选择一个DBMS而不是另一个。
  3. 当存在标准时(例如,需要标准JPA时使用Hibernate特定功能),出于性能原因决定使用特定的、非可移植的API。
  4. 为了提高性能而编写汇编代码。
  5. 为了提高性能而展开循环。
  6. 编写非常快但晦涩难懂的代码。

我的底线很简单。优化是一个广泛的术语。当人们谈论过早优化时,他们并不是说你需要只考虑头脑中浮现的第一件事而不考虑整体情况。他们是在说你应该:

  1. 专注于80/20法则-不要考虑所有可能的情况,而是最有可能的情况。
  2. 没有充分的理由就不要过度设计东西。
  3. 如果没有真正的、即时的性能问题,不要编写不清晰、不简单、不易维护的代码。

这真的取决于你的经验。如果你是图像处理方面的专家,有人要求你做一些你以前做过十次的事情,你可能会从一开始就推出所有已知的优化,但那没关系。过早地进行优化是指在不知道需要进行优化的情况下尝试优化某些东西。原因很简单-它很冒险,浪费你的时间,并且难以维护。所以,除非你有经验并且之前走过这条路,请不要在不知道存在问题的情况下进行优化。


1

请注意,优化并非免费(就像啤酒一样)

  • 编写需要更多的时间
  • 阅读需要更多的时间
  • 测试需要更多的时间
  • 调试需要更多的时间
  • ...

所以,在对任何东西进行优化之前,你应该确信它值得这么做。

你链接的 Point3D 类型似乎是某个东西的基石,优化的理由可能很明显。

就像 .NET 库的创建者在开始优化 System.String 之前不需要任何测量一样。但在进行过程中必须进行测量。

但大多数代码对最终产品的性能没有起到重要作用。这意味着任何优化的努力都是浪费。

除此之外,大多数“过早优化”都是未经测试/未经衡量的黑客。


1

如果你在实现的早期阶段花费太多时间设计优化,那么优化就是过早的。在早期阶段,你有更好的事情需要担心:实现核心代码、编写单元测试、系统之间的通信、用户界面等等。优化是有代价的,你可能会浪费时间在不需要优化的东西上,同时创建出更难维护的代码。

只有当你的项目具有明确的性能要求时,优化才有意义,而且在初始开发后,当你已经实现了足够的系统以实际测量所需测量的任何内容时,性能才会有影响。永远不要在没有测量的情况下进行优化。

随着你的经验增加,你可以在早期设计和实现时略微考虑未来的优化,也就是说,尝试以这样的方式设计,使得以后更容易测量性能并进行优化,如果有必要的话。但即使在这种情况下,你也应该在开发的早期阶段花费很少的时间进行优化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接