简单vs复杂(但性能高效)解决方案-选择哪个,何时选择?


28

我已经编程了两年,经常发现自己陷入了困境。

有两种解决方案-

  • 一种是简单的,即简单的方法,易于理解和维护。它涉及一些冗余,一些额外的工作(额外的IO,额外的处理),因此不是最佳的解决方案。
  • 但是其他方法则使用复杂的方法,难以实现,通常涉及许多模块之间的交互,是一种高效的解决方案。

当我没有硬性能SLA甚至简单的解决方案都可以满足性能SLA时,我应该争取哪种解决方案?我对开发人员的简单解决方案不屑一顾。

如果您的性能SLA可以通过一个简单的解决方案来解决,那么提出最佳最佳复杂解决方案是一种好习惯吗?


10
请参阅:如何避免“开发人员的不良优化直觉”?“不幸的是,开发人员通常会对应用程序中的性能问题实际存在的地方有着直觉。
gnat 2012年

1
“不是最理想的”还会“足够好”吗?然后就这样。

8
是的 “ Le euest est l'ennemi du bien。 ”伏尔泰。(“完美是善的敌人。”足够好就足够了-直到性能测试表明不是这样。
David Hammen 2012年

我发现(通常)简单意味着有效。因此,通常不需要妥协。
2012年

2
“似乎没有更多的东西可以实现完美,而只有更多的东西可以去除。” –安东尼·圣艾修伯里
keuleJ 2012年

Answers:


58

当我没有硬性能SLA甚至简单的解决方案都可以满足性能SLA时,我应该争取哪种解决方案?

简单的一个。它符合规格,更易于理解,更易于维护,并且可能少了很多错误。

提倡高效性能解决方案的目的是将推测性的通用性和过早的优化引入代码中。别做!性能与几乎所有其他软件工程的“灵活性”(可靠性,可维护性,可读性,可测试性,易理解性……)背道而驰。测试时追逐性能表明确实需要追逐性能。

当性能无关紧要时,不要追求性能。即使确实很重要,您也应该只在测试表明存在性能瓶颈的区域追逐性能。simple_but_slow_method_to_do_X()如果该简单版本没有成为瓶颈,则不要以性能问题为由来取代一个更快的版本。

几乎无可避免地会遇到许多代码气味问题,从而影响了性能。您在问题中提到了几个:复杂的方法,难以实现,耦合性更高。那些真的值得一试吗?


您的回答非常有帮助
MoveFast 2012年

1
尽可能简单,但不简单;尽可能快,但不要更快;等等
user606723

2
“如有疑问,请使用蛮力”;)
tdammers 2012年

在此,代码中的注释既可以宣泄,也可以有所帮助。简短的注释指出复杂+快速的解决方案,以及为什么不使用它,可以让您感觉就像您忽略了最佳算法。它可以帮助维护人员了解您的选择,并在以后确实需要优化时将它们指向正确的方向。
TheAtomicOption '18

12

简短答案:相对于复杂,更喜欢简单的解决方案,并记住KISS和YAGNI的原理

由于最初的项目要求和软件永远都不完美,因此在开发/使用应用程序时需要进行更改。开发阶段的迭代方法非常适合从简单开始并根据需要进行扩展。最简单的解决方案具有灵活性的空间,并且更易于维护。

此外,尝试精打细算并在仍在构建应用程序的同时进行一些临时优化不是一个好习惯,并且可能会使解决方案过于复杂。众所周知,"premature optimization is the root of all evil"-从努斯的书中


1
@ManojGumber,没问题,这实际上是程序员应首先关心的本质。
EL Yusubov 2012年

8

从库努斯(Knuth)那里汲取教训:“我们应该忘记效率低下的问题,大约有97%的时间是这样:过早的优化是万恶之源”。

请按以下顺序考虑您的解决方案:首先,始终是正确性。第二,提高清晰度和简洁性。第三,只有当您可以证明需要时,效率。

提高效率几乎总是会花费您一些重要的事情,因此只有在您知道需要时才应该这样做。


4
请注意,这并不意味着您不应该首先编写一个好的实现。

@ThorbjørnRavnAndersen:当然,这就是前两点。
simon

1
@simon引号经常被用作

关于您的第二点:我有一位同事经常说他在正确的意大利面条之前喜欢结构不正确,代码干净的错误代码。
Buhb 2012年

@ThorbjørnRavnAndersen不称职的人会以任何借口为借口。对原始思想的价值没有任何影响。
simon 2012年

7

简单是可靠性的前提。如果您有一个可行的简单解决方案,那就一定要去!优化工作程序比使优化程序工作容易得多。同样不要忘记摩尔定律:如果您的简单解决方案满足当今的性能目标,则可能会在一两年内将它们粉碎1次


1那里没有保证,因为正如吉米·霍法Jimmy Hoffa)在下面的评论中指出的那样,摩尔定律有其局限性。


您忘记了摩尔的另一条法律,该法律规定:“糟糕,关于我的第一部法律。。”对不起,老板,摩尔定律不再适用。我不会不同意您的其余观点,至少请留意最后一部分。
Jimmy Hoffa 2012年

2
抱歉,但是根据我在该行业的所有经验。“工作集”增加FAR的速度快于我们不断升级的硬件的速度。真的,我只是删除摩尔定律。
user606723 2012年

@ user606723“工作集”点的增长与“优化或简单”问题成正比:无论采用哪种解决方案,工作量都会跟上他们的步伐。将摩尔定律引入混合的目的是要指出,即使在撰写本文时,简单的解决方案在性能压力下,随着更快的硬件可用,压力也会降低。
dasblinkenlight 2012年

@dasblinkenlight,工作集的增长与摩尔定律与问题的正交性不那么正交。使工作集陷入问题的要点是,如果一个简单的解决方案在发布时处于性能压力之下,则在不久的将来性能将不足,因为不断增加的工作集将破坏通过改进的硬件实现的任何性能改进。尽管我只希望拥有简单,可靠且可维护的软件,但发布时已经承受性能压力并期望摩尔定律使之稳定的发布软件是一个可怕的哲学。
user606723 2012年

3

如果您的性能SLA可以通过一个简单的解决方案来解决,那么提出最佳最佳复杂解决方案是一种好习惯吗?

最优是一个模棱两可的词!

最终,如果必须维护复杂的主机存在很大的风险,并且如果简单的主机“足够好”,我总是会犯错。

如果再加上复杂性不够好的任何风险,那么KISS可能就是正确的答案。


2

我更喜欢简单的一种。在我看来,过早的优化会解决许多问题。在许多情况下,良好的设计可以让您在将来遇到瓶颈时更改给定的实现。

因此,最重要的是-我将尽可能地设计它的灵活性,但不会为灵活性而牺牲太多的简单性。


2

哪一个成本更低?

在大多数情况下,稍微慢一点的简单解决方案就性能而言是完全可以接受的,而简单性使得开发,维护和最终更换便宜。

另一方面,有时速度确实很重要,即使很小的速度改进所带来的经济收益也可能远远大于更复杂的解决方案所增加的成本。例如,将完成交易的时间缩短0.01s,可以使证券交易系统的利润更高。支持数百万个用户的系统的效率提高10%可能意味着服务器成本将大大降低。

因此,您必须问自己的问题是:使用复杂的解决方案是否会对底线产生足够的影响以支付其额外费用?实际上,您可能应该请客户决定,因为他们要支付账单并获得潜在收益。一个好的选择是首先使用简单的解决方案,并提供更复杂的解决方案作为可能的改进。这样一来,您就可以启动系统并运行它,并为客户提供一些开始测试的经验,而这种经验可能会决定实施(或不实施)更复杂的解决方案的决定。


2

在评估两种方法时,一种方法更简单但效率较低,而另一种方法更复杂且效率更高,一种方法必须考虑问题和项目领域。

考虑一个针对医疗保健行业的数十亿美元的软件项目,该项目的计划使用寿命是15年以上的维护和+20年的使用。在这样的项目中,性能绝对不是问题,但是项目的复杂性和结构会给项目的维护带来重大问题,该项目的维护至少要持续15年。可维护性和简单性比任何事情都要重要。

然后,考虑另一个示例。一个控制台游戏引擎,预计将在未来5年以上为公司即将推出的游戏提供动力。因为游戏非常资源限制的程序,因此在许多情况下,效率要比可维护性高。为某些任务编写自己的,非常具体的数据结构和算法可能非常重要,即使这违背了软件开发的任何“最佳实践”。面向数据的设计就是一个很好的例子,其中您将数据存储在相似的数据数组中,而不是存储在实际的对象中。这是为了增加局部性的参考,并因此提高CPU缓存效率。不切实际,但在给定领域中非常关键。


1

这始终是一个棘手的问题,我看到答案在一个方向摇摆,所以我将在另一端玩游戏,尽管我并不认为任何一个答案都是正确的,但这是一个非常软和逐案的主题。

关于复杂但高性能的解决方案的一件事是,您始终可以仅记录其中永远存在的问题。我通常是自记录代码的爱好者,但是我还是软件的爱好者,它会在一定时间内做出响应,这使我觉得它并没有让我放慢脚步。如果确实要使用复杂但高性能的解决方案,请考虑可以采取什么措施使其变得不那么糟糕:

将其包装在接口中,将其自己放在一个程序集中,甚至可能将其全部放在一个进程中。使它尽可能松散地耦合,并在其周围尽可能厚的抽象壁以避免泄漏。为此编写大量的单元测试以保存将来的回归。

将其记录在代码中,甚至考虑编写一些真实的文档。考虑一下复杂的数据结构以及如何记录它们,想象一下尝试从没有数据结构书/维基百科文章的代码中理解其中一个的实现。但是我们都接受这些复杂的数据结构实际上是一件好事,有人用我们的语言实现它们是有益的。

请记住,我们所有人都是在TCP / IP堆栈上发送消息,如果我们中的任何一个人看的话,代码可能会变得很麻烦,这显然可以使它执行我们都需要的方式。也许您的问题不需要这种优化级别,但是可能需要,但是在解决这个问题时要当心,因为我们所有人都必须时不时地:那里有小龙。


0

我要在没有性能SLA的领域进行这项工作。对于计算机图形学中的脱机渲染器,用户没有“令人满意的性能”,因为他们已经投入了大量资金,即使使用最先进的渲染器,也可以跨云分布计算并渲染服务器场输出高质量的胶片图像和帧,例如

但我必须说,作为在这一领域工作了多年的人,任何严重降低可维护性以提高效率的解决方案实际上都在应对日新月异的性能要求。因为如果随着情况的发展(在周围的代码以及用户对竞争对手的竞争中不断取得的成就,用户的期望都很高),您无法在未来几年内有效地维护您的解决方案,那么您的解决方案已经在朝着过时的方向发展。需要批发更换。

我不认为像VTune这样的探查器的最终目的是使我的代码运行得更快。它们的终极价值是确保我不会降低生产力以满足不断增长的性能要求。如果我绝对必须进行一些粗略的微优化,那么探查器结合针对实际用户案例运行(我认为不是某些测试用例可能很重要),可以确保我不可避免地应用这种粗略的外观非常明智地仅对出现的最重要的热点进行优化,并非常仔细地记录下来,因为如果该解决方案仍然可行,我将不可避免地不得不在接下来的几年中重新审视,维护和调整并更改它们。

特别是如果您的优化解决方案涉及更多耦合,那么我真的不愿意使用它。在代码库中对性能最关键的领域中,我最欣赏的最有价值的指标之一就是去耦(例如,在使需要处理的信息量最小化的同时,这也最小化了需要更改的可能性,除非直接需要更改) ),因为这些关键区域显着增加了事物发生变化的原因。这意味着某些事情需要工作的信息越少,变更的原因就越少,而将变更的原因降到最低确实是提高我特定关注领域的生产力的重要组成部分,因为无论如何情况都将不断变化(我们'否则会在一年后过时),

对我来说,我找到的最大,最有效的解决方案是,效率,可维护性和生产率并非彼此截然相反。对我的追求是,试图使这些概念尽可能地和谐。

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.