【问题标题】:When is optimization premature? [closed]什么时候优化还为时过早? [关闭]
【发布时间】:2011-06-17 11:41:22
【问题描述】:

我看到这个词被大量使用,但我觉得大多数人使用它是出于懒惰或无知。例如,我正在阅读这篇文章:

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

他谈到了他为实现应用所需的类型而做出的决定。

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

  1. 当什么都没有时,我想的太多了,因此过早地进行了优化。
  2. 在没有减速或性能问题的情况下过度考虑无关紧要的细节。

或两者兼而有之。

并建议只实施它,在它们成为问题之前不要担心这些。

哪个更优惠?

对于性能关键型应用程序,在任何实施完成之前,如何区分过早优化和明智决策?

【问题讨论】:

标签: c# .net performance optimization premature-optimization


【解决方案1】:

如果出现以下情况,则优化为时过早:

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

  2. 你正在做一些非装配的时间紧迫的事情,并且仍然担心i++; i++; 更快还是i += 2...如果它真的那么关键,你'会在组装工作,而不是浪费时间担心这个。 (即便如此,这个特定的例子很可能并不重要。)

  3. 您有一种预感,即一件事可能比另一件事快一点,但您需要查一下。例如,如果您对 StopWatchEnvironment.TickCount 是否更快感到困扰,这是过早的优化,因为如果差异更大,您可能会更加确定并且不需要查找它。

如果您猜测某事可能会很慢,但您不太确定,只需添加//NOTE: Performance? 评论,如果您稍后遇到瓶颈,请检查代码中的此类位置。我个人并不担心不太明显的优化;如果需要,我稍后会使用分析器。

另一种技巧:

我只是运行我的程序,用调试器随机中断它,看看它停在哪里——停在哪里都可能是瓶颈,它停在那里的次数越多,瓶颈就越严重。它几乎像魔术一样工作。 :)

【讨论】:

  • 谢谢,这是一个很好的技术。
  • 拥有现代编译器的水平,你需要非常精通汇编(更不用说你目标的架构大师,在理解CPU、总线、RAM等方面)打败他们。
  • +1 @Eldad:是的,当然。 :)
  • ++ 你的最后一段是我所依赖的方法。由于它有很多疑问,我尝试了to explain it 和很多statistical arguments
  • +1 Massive plus 1 on your last paragraph,这是我最喜欢的调试技术之一 ;-)
【解决方案2】:

这句谚语(我相信)并不是指在创建时就内置于良好设计中的优化。它是指专门针对绩效的任务,否则不会执行。

根据常识,这种优化不会“变得”过早——在被证明是无辜的之前,它是有罪的。

【讨论】:

    【解决方案3】:

    优化是使现有代码更有效地运行(更快的速度和/或更少的资源使用)的过程

    如果程序员没有证明有必要,

    所有优化都为时过早。 (例如,通过运行代码来确定它是否在可接受的时间范围内实现了正确的结果。这可能就像运行它以“查看”它是否运行得足够快,或者在分析器下运行以更仔细地分析它一样简单) .

    良好的编程有几个阶段:

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

    2) 以可维护、编码良好的方式实施解决方案。

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

    4)如果不符合要求,考虑原因。在大多数情况下,这意味着在您更好地理解问题后,转到步骤 (1) 以找到更好的算法。 (编写一个快速原型通常是一种低成本探索的好方法)

    5) 如果仍然不满足要求,开始考虑可能有助于加快运行时的优化(例如,查找表、缓存等)。为了推动这个过程,profiling 通常是帮助您定位代码中的瓶颈和低效率的重要工具,因此您可以在代码上花费的时间获得最大收益。 p>

    我应该指出,处理一个相当熟悉的问题的有经验的程序员可能能够在头脑中跳过第一步,然后只是应用一个模式,而不是每次都在物理上经历这个过程,但这只是一个简短的通过经验获得的削减

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

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

    例如,我刚刚替换了我们项目中的一段旧代码。我的新代码没有以任何方式“优化”,但是(与原始实现不同)它是在考虑效率的情况下编写的。结果:我的运行速度提高了 25 倍 - 只是不浪费。我可以优化它以使其更快吗?是的,我可以轻松获得另外 2 倍的加速。我会优化我的代码以使其更快吗?不——5 倍的速度提升就足够了,而我已经达到了 25 倍。此时的进一步工作只会浪费宝贵的编程时间。 (但如果需求发生变化,我可以在将来重新访问代码)

    最后一点:您工作的领域决定了您必须达到的标准。如果您正在为游戏编写图形引擎或为实时嵌入式控制器编写代码,您可能会发现自己做了很多优化。如果您正在编写像记事本这样的桌面应用程序,那么只要您不过分浪费,您可能永远不需要优化任何东西。

    【讨论】:

    • 谢谢,顺便说一句,我修正了一些错别字,希望你不要介意。
    • @Joan Venge:不用担心 - 我总是在这个脆弱的笔记本电脑键盘上遗漏字符 :-)
    【解决方案4】:

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

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

    但是,在您发现实际问题之前,不要担心。

    【讨论】:

      【解决方案5】:

      过早的优化是在您知道有必要做出这种权衡之前,以牺牲代码的其他一些积极属性(例如可读性)为代价进行性能优化。

      通常在开发过程中进行过早的优化,而不使用任何分析工具来查找代码中的瓶颈。在许多情况下,优化会使代码更难维护,有时还会增加开发时间,从而增加软件成本。更糟糕的是......一些过早的优化结果根本不会使代码变得更快,在某些情况下甚至会使代码比以前慢。

      【讨论】:

      • 嗯,有时你应该“优化”,即使你不一定需要它:例如,我会说你永远不应该使用ArrayList 代替int 而不是List<int>,甚至如果它对您的特定程序没有太大影响。 (但如果你想知道,我不是给 -1 的人。)
      • @Mehrdad:这更多的是可维护性问题,而不是优化。
      • @R. Bemrose:两者兼有——避免装箱/拆箱当然是一种优化,对我来说,这是比可读性/可维护性更重要的原因。
      • 我会将类型安全与性能放在同一水平上——我在 Java 中经常避免使用 ArrayList<Integer>(我使用其他使用 int[] 的类),即使它是类型安全的.
      【解决方案6】:

      当您的编码经验不足 10 年时。

      【讨论】:

        【解决方案7】:

        拥有(很多)经验可能是一个陷阱。我认识许多非常有经验的程序员(C\C++、汇编),他们往往会担心太多,因为他们习惯于担心时钟滴答和多余的位。

        在某些领域,例如嵌入式或实时系统,这些确实很重要,但在常规 OLTP/LOB 应用程序中,您的大部分工作都应该针对可维护性、可读性和可更改性。

        【讨论】:

          【解决方案8】:

          优化很棘手。考虑以下示例:

          1. 决定实施两台服务器,每台服务器各司其职,而不是实施一台同时完成这两项工作的服务器。
          2. 出于性能原因,决定使用一个 DBMS 而不是另一个。
          3. 出于性能原因,决定在有标准时使用特定的、不可移植的 API(例如,当您基本上需要标准 JPA 时使用特定于 Hibernate 的功能)。
          4. 出于性能原因在汇编中进行编码。
          5. 出于性能原因展开循环。
          6. 编写一段非常快速但晦涩难懂的代码。

          我的底线很简单。优化是一个广义的术语。当人们谈论过早的优化时,他们并不意味着您只需要做首先想到的事情而不考虑整体情况。他们说你应该:

          1. 专注于 80/20 规则 - 不要考虑所有可能的情况,而要考虑最可能的情况。
          2. 不要无故过度设计东西。
          3. 如果没有真正的、直接的性能问题,请勿编写不清晰、简单且易于维护的代码。

          这一切都归结为您的经验。如果你是图像处理方面的专家,并且有人要求你做你之前做过十次的事情,你可能会从一开始就推动所有已知的优化,但这没关系。过早的优化是当您尝试优化某些东西时,您不知道它需要从一开始就进行优化。原因很简单——它有风险,浪费你的时间,而且更难维护。因此,除非您有经验并且您以前曾走过这条路,否则如果您不知道存在问题,请不要进行优化。

          【讨论】:

            【解决方案9】:

            注意优化不是免费的(就像啤酒一样)

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

            所以在优化任何东西之前,你应该确定它是值得的。

            您链接到的那个 Point3D 类型似乎是某些东西的基石,优化的情况可能很明显。

            就像 .NET 库的创建者在开始优化 System.String 之前不需要任何测量一样。不过,他们必须在此期间进行测量。

            但大多数代码在最终产品的性能中并没有发挥重要作用。这意味着任何优化的努力都被浪费了。

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

            【讨论】:

              【解决方案10】:

              如果您在实施的早期阶段花费太多时间来设计优化,则为时过早。在早期阶段,您需要担心一些更好的事情:实现核心代码、编写单元测试、系统相互通信、UI 等等。优化是有代价的,你很可能在优化一些不需要的东西上浪费时间,同时创建更难维护的代码。

              只有当您对项目有具体的性能要求时,优化才有意义,然后性能会在初始开发之后变得很重要,并且您已经实现了足够的系统,以便实际衡量您需要衡量的任何内容。永远不要在没有测量的情况下进行优化。

              随着您获得更多经验,您可以在进行早期设计和实施时关注未来的优化,也就是说,尝试以一种更容易衡量性能并在以后进行优化的方式进行设计,如果那样的话甚至是必要的。但即使在这种情况下,您也应该在开发的早期阶段花很少的时间进行优化。

              【讨论】:

                猜你喜欢
                • 2010-09-27
                • 1970-01-01
                • 2020-02-11
                • 2010-10-17
                • 1970-01-01
                • 2013-08-05
                • 1970-01-01
                • 2012-09-05
                • 1970-01-01
                相关资源
                最近更新 更多