什么时候优化为时过早?
我看到这个词用得很多,但我觉得大多数人使用它是出于懒惰或无知。例如,我正在阅读这篇文章:
http:// blogs.msdn.com/b/ricom/archive/2006/09/07/745085.aspx
,他在其中谈论了他为实现应用程序所需类型而做出的决定。
如果是我,在我们需要编写的代码中谈论这些,其他程序员可能会这样想:
- 我在什么都没有的情况下考虑太多,从而过早地优化。
- 当没有遇到速度减慢或性能问题时,过度考虑无关紧要的细节。
或两者兼而有之。
并建议只实施它,不要担心这些,直到它们成为问题。
哪个更优惠?
在完成任何实施之前,如何区分性能关键型应用程序的过早优化与明智决策?
I see this term used a lot but I feel like most people use it out of laziness or ignorance. For instance, I was reading this article:
http://blogs.msdn.com/b/ricom/archive/2006/09/07/745085.aspx
where he talks about his decisions he makes to implement the types necessary for his app.
If it was me, talking about these for code that we need to write, other programmers would think either:
- I am thinking way too much ahead when there is nothing and thus prematurely optimizing.
- Over-thinking insignificant details when there is no slowdowns or performance problems experienced.
or both.
and would suggest to just implement it and not worry about these until they become a problem.
Which is more preferential?
How to make the differentiation between premature optimization vs informed decision making for a performance critical application before any implementation is done?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
发布评论
评论(10)
优化是使现有代码运行更高效(更快的速度和/或更少的资源使用)的过程,
如果程序员没有证明有必要,那么所有优化都是不成熟的。 (例如,通过运行代码来确定它是否在可接受的时间范围内实现了正确的结果。这可以像运行它来“查看”它是否运行得足够快一样简单,或者在探查器下运行以更仔细地分析它) 。
良好的编程有几个阶段:
1) 设计解决方案并选择一个好的、高效的算法。
2) 以可维护、编码良好的方式实施解决方案。
3) 测试该解决方案,看看它是否满足您对速度、RAM 使用情况等的要求(例如“当用户单击“保存”时,花费的时间是否少于 1 秒?”如果需要 0.3 秒,那么您确实不需要)不需要花一周的时间优化它才能将时间降低到0.2s)
4) 如果不满足要求,请考虑原因。在大多数情况下,这意味着您现在已经更好地理解了问题,请转到步骤 (1) 寻找更好的算法。 (编写快速原型通常是一种廉价探索的好方法)
5)如果它仍然不满足要求,开始考虑可能有助于加快运行时间的优化(例如,查找表、缓存等)。为了推动这一过程,分析通常是一个重要的工具,可以帮助您找到代码中的瓶颈和低效率,这样您就可以从花在代码上的时间获得最大的收益。
我应该指出,一个经验丰富的程序员处理一个相当熟悉的问题可能能够在精神上跳过第一步,然后只应用一种模式,而不是每次都亲自经历这个过程,但这只是一个捷径,通过经验获得
因此,有经验的程序员会自动将许多“优化”构建到他们的代码中。这些与其说是“过早的优化”,不如说是“常识性的效率模式”。这些模式快速且易于实现,但极大地提高了代码的效率,并且您不需要进行任何特殊的计时测试来确定它们是否有好处:
- 不将不必要的代码放入循环中。 (类似于从现有循环中删除不必要的代码的优化,但它不涉及编写两次代码!)
- 将中间结果存储在变量中,而不是一遍又一遍地重新计算。
- 使用查找表提供预先计算的值,而不是动态计算它们。
- 使用适当大小的数据结构(例如,以字节(8 位)而不是长整型(64 位)形式存储百分比将使用更少的 RAM 8 倍)
- 使用预先绘制的图像绘制复杂的窗口背景,而不是绘制大量单独的窗口背景组件
- 对要通过低速连接发送的数据包应用压缩,以最大限度地减少带宽使用。
- 以允许您使用可以获得高质量和良好压缩的格式的样式为您的网页绘制图像。
- 当然,尽管这在技术上不是“优化”,但首先要选择正确的算法!
例如,我刚刚替换了我们项目中的一段旧代码。我的新代码没有以任何方式“优化”,但是(与原始实现不同)它是在编写时考虑到效率的。结果:我的运行速度快了 25 倍 - 仅仅是因为不浪费。我可以优化它以使其更快吗?是的,我可以轻松获得另外 2 倍的加速。我会优化我的代码以使其更快吗?不 - 速度提高 5 倍就足够了,我已经达到了 25 倍。此时进一步的工作只会浪费宝贵的编程时间。 (但如果需求发生变化,我可以在将来重新访问代码)
最后,最后一点:您正在工作的领域决定了您必须满足的标准。如果您正在为游戏编写图形引擎或为实时嵌入式控制器编写代码,您可能会发现自己做了很多优化。如果您正在编写像记事本这样的桌面应用程序,那么只要不过度浪费,您可能永远不需要优化任何内容。
优化是很棘手的。考虑以下示例:
- 决定实现两个服务器,每个服务器执行自己的工作,而不是实现一个服务器来完成这两项工作。
- 出于性能原因,决定使用一种 DBMS 而不是另一种。
- 出于性能原因,在有标准时决定使用特定的、不可移植的 API(例如,当您基本上需要标准 JPA 时使用 Hibernate 特定的功能)。
- 出于性能原因,在汇编中编码某些内容。
- 出于性能原因展开循环。
- 编写一段非常快但晦涩的代码。
我的底线很简单。优化是一个广义的术语。当人们谈论过早优化时,他们并不意味着您需要只做想到的第一件事而不考虑整体情况。他们说你应该:
- 专注于 80/20 规则 - 不要考虑所有可能的情况,而是考虑最可能的情况。
- 没有充分理由就不要过度设计东西。
- 如果没有真正的、直接的性能问题,就不要编写不清晰、简单且易于维护的代码。
这实际上一切都取决于您的经验。如果您是图像处理方面的专家,并且有人要求您做一些您以前做过十次的事情,您可能会从一开始就推动所有已知的优化,但这没关系。过早优化是指当您尝试优化某项内容时,您一开始并不知道它需要优化。原因很简单——它有风险,浪费你的时间,而且更难维护。因此,除非您经验丰富并且以前曾走过这条路,否则在不知道存在问题的情况下不要进行优化。
请注意,优化不是免费的(就像啤酒一样)
- 它需要更多的时间来编写
- ,需要更多的时间来阅读
- ,需要更多的时间来测试
- ,需要更多的时间来调试
- ......
所以在优化任何东西之前,你应该确保它是值得的。
您链接到的 Point3D 类型似乎是某些东西的基石,并且优化的情况可能是显而易见的。
就像 .NET 库的创建者在开始优化 System.String 之前不需要任何测量一样。但他们必须在此期间进行测量。
但大多数代码对最终产品的性能并没有发挥重要作用。这意味着优化方面的任何努力都被浪费了。
除此之外,大多数“过早的优化”都是未经测试/未经测量的黑客行为。
如果您在实施的早期阶段花费太多时间来设计优化,那么优化就为时过早。在早期阶段,您有更好的事情需要担心:实现核心代码、编写单元测试、系统相互通信、UI 以及其他任何事情。优化是有代价的,您很可能会浪费时间来优化不需要的东西,同时创建更难维护的代码。
仅当您的项目有具体的性能要求时,优化才有意义,并且在初始开发之后,性能将很重要,并且您已经实施了足够的系统,以便实际测量您需要测量的任何内容。切勿在没有测量的情况下进行优化。
随着您获得更多经验,您可以在进行早期设计和实现时着眼于未来的优化,也就是说,尝试以这样的方式进行设计,以便更容易衡量性能并在以后进行优化(如果有必要的话) 。但即使在这种情况下,您也不应该在开发的早期阶段花很少的时间进行优化。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
如果出现以下情况,优化就为时过早:
您的应用程序没有执行任何对时间要求严格的操作。 (这意味着,如果您正在编写一个将文件中的 500 个数字相加的程序,那么“优化”这个词根本不应该出现在您的大脑中,因为它只会浪费您的时间。)
您正在做一些除汇编之外的时间紧迫的事情,并且仍然担心
i++; 是否有效。 i++;
更快,或者i += 2
...如果它真的那么重要,那么您将在汇编中工作,而不是浪费时间担心这个。 (即使如此,这个特定的示例很可能并不重要。)您有一种预感,一件事可能比另一件事快一点,但您需要查找它。例如,如果您对
StopWatch
更快还是Environment.TickCount
哪个问题感到困扰,那么这是过早的优化,因为如果差异更大,您可能会更确定并且不需要查找它。如果您猜测某些事情可能会很慢但又不太确定,只需添加
//NOTE: Performance?
注释,如果稍后遇到瓶颈,请检查代码中的此类位置。我个人并不担心不太明显的优化;如果需要的话,我稍后会使用分析器。另一种技术:
我只是运行我的程序,用调试器随机中断它,然后查看它在哪里停止 - 它停止的地方可能是瓶颈,并且在那里停止的次数越多,瓶颈就越严重。它的作用几乎就像魔术一样。 :)
Optimization is premature if:
Your application isn't doing anything time-critical. (Which means, if you're writing a program that adds up 500 numbers in a file, the word "optimization" shouldn't even pop into your brain, since all it'll do is waste your time.)
You're doing something time-critical in something other than assembly, and still worrying whether
i++; i++;
is faster ori += 2
... if it's really that critical, you'd be working in assembly and not wasting time worrying about this. (Even then, this particular example most likely won't matter.)You have a hunch that one thing might be a bit faster than the other, but you need to look it up. For example, if something is bugging you about whether
StopWatch
is faster orEnvironment.TickCount
, it's premature optimization, since if the difference was bigger, you'd probably be more sure and wouldn't need to look it up.If you have a guess that something might be slow but you're not too sure, just put a
//NOTE: Performance?
comment, and if you later run into bottlenecks, check such places in your code. I personally don't worry about optimizations that aren't too obvious; I just use a profiler later, if I need to.Another technique:
I just run my program, randomly break into it with the debugger, and see where it stopped -- wherever it stops is likely a bottleneck, and the more often it stops there, the worse the bottleneck. It works almost like magic. :)