开发人员通常认为软件性能遵循帕累托分布:80% 的运行时间花在 20% 的代码上。使用此模型,您可以编写大部分代码而不用关心性能,而是专注于对性能敏感的狭窄代码段。像 Casey Muratori 这样的工程师正确地批评了这种模型。你可以在他的博客上阅读 Muratori 的优秀文章。
确实并非所有代码都需要注意。例如,您的代码可能在 99% 的时间处理正确的数据。处理错误的代码可能会非常慢,并且不会影响您的大多数用户。
但热点预测了更准确的事情:您应该能够只保留所有代码,识别特定的瓶颈,优化这些瓶颈,然后全面获得出色的性能。 Muratori 依靠经验证据来伪造模型:许多公司开始对其代码库进行大规模重写以对其进行优化。如果他们可以简单地找出瓶颈并解决这些问题,为什么还要为这些费用烦恼呢?
我们现在有称为分析器的工具,可以大致告诉您软件将时间花在哪里。如果你将这样的工具应用到你的软件中,你可能确实会发现巨大的瓶颈。它有时可能会产生奇迹。例如,有一个正在加载 JSON 文件的视频游戏 ( GTA Online )。简单地优化这个瓶颈就解决了一个巨大的性能问题。它并没有使游戏性能更高,但它启动得更快。所以瓶颈确实存在。我们应该找到它们并优化它们。但这不是热点模型所预测的:它预测您需要做的就是获得性能。遇到一些瓶颈,你就完成了。
可悲的是,它不起作用。
让我们总结一下几个原因:
- 整体架构胜过一切。如果你先造了一辆公共汽车,你就不能把它变成一辆跑车。几年前,一家公司来找我,如果我能使他们的数据库引擎更快,就可以给我一大笔钱。他们有钱,但他们的软件太慢了。起初我对这个项目很兴奋,但我开始阅读他们的代码并做基准测试,然后我意识到整个架构是错误的。他们坚持认为他们知道热点在哪里,他们只需要专业知识来优化这几个组件。他们告诉我,他们的代码在大约 100 行中花费了 80% 的时间。这就是分析人员所说的。诚然,正式地说,如果你能让这 100 行代码的速度提高一倍,代码的运行速度就会提高一倍……但是这些从内存和软件中提取数据的代码行无法击败物理。有一些时间不可压缩的基本操作:您移动数据的速度不能超过软件允许的速度。关键是,如果你的软件没有一个好的整体架构,如果它没有很好地组织性能,你可能别无选择,只能从头开始重写它,重新架构它。
- 随着您的优化,热点会成倍增加。回到 GTA 在线模式的例子,很容易发现程序用了 10 秒来加载一个 10 MB 的 JSON 文件。然而,接下来的步骤将更加困难。你会发现找到瓶颈变得很困难:我们遵循海森堡原则:测量大的影响很容易,测量小的影响变得不可能,因为测量的动作与软件执行相互作用。但即使你能找到瓶颈,它们也会随着每次迭代而变得越来越多。最终,您的大部分代码都需要考虑。
优化代码所需的工作呈指数级增长。换句话说,要将性能乘以 N,需要进行 2 N次优化。您优化的越多,您必须考虑的代码就越多。将未优化的代码段的性能提高一倍相对容易,但将其乘以 10 就困难得多。您很快就会遇到无法逾越的障碍:再次将性能提高一倍所需的努力太多了。
这也解释了为什么公司会为了性能而完全重写他们的代码:从现有代码中榨取更多性能所需的努力变得太多,而完全重写更便宜。
更多内容: Federico Lois — 高性能 C# 模式。
原文: https://lemire.me/blog/2023/04/27/hotspot-performance-engineering-fails/