【问题标题】:Practical rules for premature optimization [closed]过早优化的实用规则
【发布时间】:2010-06-04 23:48:24
【问题描述】:

过早优化”这句话似乎是当时的流行语。出于某种原因,尤其是 iphone 程序员似乎将避免过早优化视为一个积极的目标,而不是简单地避免分心的自然结果。问题是,该术语开始越来越多地用于完全不合适的情况。

例如,我看到越来越多的人说不要担心算法的复杂性,因为那是过早的优化(例如Help sorting an NSArray across two properties (with NSSortDescriptor?))。坦率地说,我认为这只是一种懒惰,对有纪律的计算机科学来说是令人震惊的。

但我突然想到,考虑到算法的复杂性和性能可能会采用汇编循环展开以及其他现在被认为不必要的优化技术。

你怎么看?我们现在是否处于在 O(n^n) 和 O(n!) 复杂度算法之间做出决定无关紧要的地步? O(n) vs O(n*n) 怎么样?

您认为什么是“过早的优化”?你有什么实际的规则来有意识或无意识地避免它?

编辑

我知道我的描述有点笼统,但我对具体、实用的规则或人们用来避免“过早优化”的最佳做法感兴趣,尤其是在 iPhone 上平台

要回答这个问题,您首先需要回答“什么是过早优化?”这个问题。由于该定义显然差异很大,因此任何有意义的答案都需要作者定义该术语。这就是为什么我真的不认为这是一个CW问题。同样,如果人们不同意,我会改变它。

【问题讨论】:

  • 声明式编程的某些方面等同于“不用担心算法复杂性”。在商业世界中,过早的优化归结为美元。例如,P * N 必须 > D * R 其中 P 是每次执行代码所增加的生产(或节省)美元; N 是新代码在其生命周期内运行的次数; D 是实施和维护优化所需的开发时间,R 是相应开发人员的工资或小时费率。

标签: iphone algorithm design-patterns


【解决方案1】:

什么是过早优化?

过早优化是在您知道是否值得这样做之前优化代码(通常是为了性能)的过程。过早优化的一个例子是在分析代码以找出性能瓶颈之前优化代码。过早优化的一个更极端的例子是在您运行程序并确定它运行得太慢之前进行优化。

我们现在是否已经到了在 O(n^n) 和 O(n!) 复杂度算法之间做出选择无关紧要的地步? O(n) vs O(n*n) 怎么样?

这取决于 n 的大小以及您的代码被调用的频率。

如果 n 始终小于 5,则渐近性能无关紧要。在这种情况下,常量的大小将更重要。对于小 n,一个简单的 O(n * n) 算法可以击败更复杂的 O(n log n) 算法。或者可测量的差异可能非常小以至于无关紧要。

我仍然认为有太多人花时间优化 90% 无关紧要的代码,而不是 10% 重要的代码。如果某些代码几乎从未被调用过,那么没有人会关心某些代码是否需要 10 毫秒而不是 1 毫秒。有时,即使您知道算法复杂性不是最优的,但只做一些简单但可行的事情并继续前进是一个不错的选择。

你花在优化很少被调用的代码上的每一小时,都比你花在添加人们真正想要的功能上的时间少一小时。

【讨论】:

  • +1 表示第一点。大量代码凭借其用例自动足够快
  • 除非是 O(Ackermann 函数) :)
  • 是的,我曾讨论过用大 O 来构建问题,因为它显然是特定于上下文的。好建议。 +1
  • 现在接受这个,因为这是一个很好的建议,并且似乎最接近于回答这个问题。不过,它比我希望的要抽象一些。答案可能是没有人对问题的务实方法进行过权威研究。如果有人知道,请发布。
【解决方案2】:

我的投票支持大多数人优化他们认为的弱点,但他们没有分析。

因此,无论您对算法的了解程度如何,也无论您您的代码编写得有多好,您都不知道在您的模块之外还发生了什么。您调用的 API 在幕后做了什么?你能保证特定的操作顺序是最快的吗?

这就是过早优化的意思。任何你认为是没有通过分析器或其他权威工具严格测试的优化(ops 的时钟周期并不是一件坏事,但它只告诉你性能特征~实际调用比时间更重要,通常),是一种过早的优化。

@k_b 在我上面说得很好,我也是这么说的。让它正确,让它简单,然后配置文件,然后调整。根据需要重复。

【讨论】:

  • 我在某种程度上同意,但显然有一个限制。我可能会选择在内存中存储一​​个数组,而不是让我的祖母在我需要时将它邮寄给我,但我认为这还不成熟。那么问题来了,你在哪里画线?以及如何以常规、可重复的方式划定界限,以便向其他团队成员解释?
  • @DougW - 我在上一篇文章中画了一条线:“让它正确,让它简单,然后配置文件,然后调整”。我认为有些事情是显而易见的(我每次运行都需要这个,通过将它保存在内存中而不是每次从磁盘读取来进行索引更有意义,并且使业务逻辑更容易〜与〜我每次都需要这个相比,我如何强制它留在 L2 缓存中)而有些东西不是(快速排序、堆排序和哈希排序)
  • @DougW ~ 添加一个人的名字,因为我已经完成了你的名字,以确保他们立即看到评论。我有一个没有通知的橙色信封,所以不得不搜索给我留言的人。
  • 感谢您的提示,但没有意识到它实际上已经掌握了这一点。我完全听到你在说什么,但我正试图更深入地挖掘。我最近的经验是,对于(尤其是 iphone)程序员来说,“显而易见”的东西正变得越来越多样化。当然,我们可以就许多案例达成一致,但我有兴趣找到可以应用于新案例的具体技术。
【解决方案3】:

优先顺序:1.它必须工作 2. 它必须是可维护的 3. 必须机器高效

那是我第一次编程课程的第一周。 1982 年

“过早优化”是在优先级 1 或 2 之前考虑优先级 3 的任何时间。

请注意,现代编程技术(抽象和接口)旨在简化这种优先级划分。

一个灰色区域:在初始设计期间,您确实必须检查您的解决方案是否天生就非常缓慢。否则,在你至少有一些工作代码之前不要担心性能。

【讨论】:

  • 比我想要的更抽象一点,但绝对是一个有价值的规则。我想我从来没有听过它用这样的话。 +1
【解决方案4】:

对于某些人来说,优化是编写代码乐趣的一部分,无论是否为时过早。我喜欢优化,为了易读性而克制自己。建议不要优化太多,适合喜欢优化的人。

尤其是 iPhone 程序员 考虑避免过早 优化作为一个积极的目标

大部分 iPhone 代码都与 UI 相关。没有太多需要优化的地方。不需要选择会导致性能不佳的糟糕设计,但是一旦开始编写好的设计,就几乎不需要优化。因此,在这种情况下,避免优化是一个合理的目标。

【讨论】:

  • 我认为我从中得到的观点是“大多数 iphone 代码是业余爱好者编写的业余爱好代码”。我想这不一定是坏事,但它引发了很多有趣的问题。 +1
  • 我的意图更多是说它是门户代码而不是爱好代码。许多应用程序将繁重的工作交给服务器,或者没有繁重的工作。有一些令人印象深刻的游戏必须高度优化,但像 facebook 和 twitter 这样的东西同样受欢迎,几乎没有优化空间。但是那里也有很多爱好代码。
  • 我觉得“业余爱好者编写的业余爱好代码”这句话含糊其辞。不包括游戏,我怀疑我使用的任何 iPhone 应用程序需要的选择不止一些优化。大多数在设计时使用框架并将网络调用推送到后台线程的应用程序在幼稚的实现中表现得可以接受。
  • 我相信“iphone程序员..认为避免过早优化作为一个积极的目标”的想法来自于 iPhone 开发新手并认为他们只能通过以下方式获得可接受的性能的 SO 用户重写核心数据或快速迭代。 90% 以上的时间,默认的 Apple 方法对于这些用户的需求来说非常快,因此避免过早优化的调用成为默认答案。
【解决方案5】:

你认为什么“为时过早” 优化”?有哪些实用规则 你习惯有意识地或 不自觉的避开?

使用敏捷方法(通过与用户交互来细化需求的快速迭代)很有帮助,因为在与用户的下一次会话之后,当前界面可能会发生巨大变化的意识可以更容易地专注于开发基本功能应用程序的功能而不是性能。

如果没有,您花费大量时间优化在与用户会话后完全丢弃的功能的几次迭代应该会给您信息。

【讨论】:

  • 我想你的意思是,在你尝试“使常见情况快速”之前,你需要确保你知道常见情况是什么?是的,我认为这符合实际规则。 +1
【解决方案6】:

算法复杂性,甚至选择,是一个应该隐藏在界面后面的问题。例如,List 是一种抽象,可以针对不同的情况以不同的效率以不同的方式实现。

有时避免过早的优化可以帮助设计,因为如果您的设计理念是以后需要优化,那么您更倾向于在抽象级别(例如列表)而不是实现(例如数组或链接)进行开发列表)级别。

除了避免分心之外,这还可以生成更简单、更易读的代码。如果对接口进行编程,以后可以交换不同的实现以进行优化。过早优化会导致实现细节可能过早暴露并与其他不应该看到这些细节的软件组件相结合的风险。

【讨论】:

  • 如果你正在编写一个像 List 这样的数据容器,并且你有众所周知的复杂性,你最好用粗体字指定它,否则有些人可能会遇到巨大的停机时间和服务器故障。
  • 关键字是“过早的”......优化很好,在正确的时间,这将是在生产之前。
  • 你需要小心过度抽象。如果需要随机访问,则将容器类隐藏在普通迭代器对后面可能会使用户代码减慢一个数量级。重要的是要了解如何使用代码,而不是将抽象作为某种教条。
【解决方案7】:

您使用哪些实用规则 有意识还是无意识地避免它?

避免不必要优化的一种方法是考虑相对成本效益:

A) 程序员优化代码的成本 + 测试所述优化的成本 + 维护由所述优化产生的更复杂代码的成本

对比

B) 升级运行软件的服务器或购买另一台(如果可扩展)的成本

如果 A >> B 考虑这样做是否正确。 [暂时忽略 B 的环境成本,这可能会或可能不会对您的组织造成影响]

这不仅仅适用于过早的优化,但它可以帮助您的开发人员认识到,花时间进行优化工作是一种成本,不应该除非在真正重要的事情上存在真正的可衡量的差异,例如:所需的服务器数量或通过改进响应时间获得的客户满意度。

如果管理层看不到降低成本的好处,而客户看不到缩短响应时间的好处,请问问自己为什么要这样做。

【讨论】:

  • 有一句话我一直记得:“优化正确的代码比纠正优化的代码更容易”
  • 我完全同意,但我正在寻找的是关于如何量化方程中的值的实际示例。实际上,您如何确保所有团队成员都有相似的估计? +1
  • @DougW 我不认为有一种简单的方法可以得出这些数字,因为要得出这些数字需要个人经验。您确实需要具有多年经验的程序员为自己进行估算,如果他们定期进行类似的优化,这会有所帮助。您几乎总是可以(请注意,我一直在使用,显然存在矛盾的空间)几乎总是可以购买更快的硬件。 ~ 但我以前错了,我又错了;)
  • @drachenstem 同意,单一解决方案本身不是问题,但这并不意味着人们没有找到好的启发式方法。在大多数情况下,您是对的,但是对于像 iphone 这样的嵌入式设备,您通常无法购买更快的硬件。
  • @DougW ~ 太真实了:iPhone,但对于大多数开发人员来说,也没有 5 年以上的发展才能真正了解他们在 iPhone 方面的能力。我认为当它是 iPhone 时,启发式变得更加重要......
【解决方案8】:

我认为这是一个常识问题。有必要了解全局,甚至了解幕后发生的事情,以便能够考虑所谓的“过早”举措何时是合理的。

我曾使用过需要调用 Web 服务来根据本地列表的内容计算新值的解决方案。实现的方式是通过对每个值进行 Web 请求。一次发送多个值并不是过早的优化。将数据库事务用于多个操作(即多次插入)也是如此。

谈到算法,最初最重要的事情是让它正确且尽可能简单。那时担心堆栈与堆问题会很疯狂。

【讨论】:

  • @k_b:我不同意:它仍然为时过早。也许那些“明显”的 Web 服务调用并不是应用程序的瓶颈。您可能会浪费时间解决错误的问题。
  • 我的意思是;为什么首先要实现这样一个低效的模块?实现每次调用发送多个值不会花费更多时间。
  • @John Saunders:如果我们正在处理 LAN,请同意,如果我们正在处理 WAN,请不要同意。当通过 WAN 调用服务时,每个 Web 服务调用的网络延迟都会立即显现出来。
  • @Marjan:也许数据库查询所花费的时间是网络延迟的 10 倍。唯一有意义的是分析并找出下一个最糟糕的问题是什么,然后去优化它。否则你可能会解决错误的问题,而不是解决正确的问题。
  • 问题是,人们在这个问题上的“常识”差异很大。在团队环境中,编码实践的极端变化是危险的。那么我们如何真正量化它呢?这就是问题所在。
猜你喜欢
  • 2011-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-17
  • 1970-01-01
  • 2011-03-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多