您如何推断基准测试数据的波动?

How do you reason about fluctuations in benchmarking data?

假设您正在尝试优化一个函数并使用一些基准测试框架(如 Google Benchmark)进行测量。您 运行 原始函数的基准测试 3 次,看到平均挂钟 time/CPU 次 100 毫秒、110 毫秒、90 毫秒。然后你 运行 对“优化”函数的基准测试 3 次,看到 80 毫秒、95 毫秒、105 毫秒。 (我编造了这些数字)。您是否得出优化成功的结论?

我经常 运行 遇到的另一个问题是,我会去做其他事情,然后 运行 当天晚些时候的基准测试,得到的数字比原始数据和原始数据之间的差值更远当天早些时候进行了优化(例如,原始函数分别为 80 毫秒、85 毫秒、75 毫秒)。

我知道有统计方法可以确定改进是否“显着”。软件工程师真的会在实践中使用这些正式计算吗?

我正在寻找优化代码时要遵循的某种流程。

Do you conclude that your optimizations were successful?

没有。 3 运行s 不够 特别是由于 巨大的变化 以及两个组的一些时间在合并后混合的事实并排序。

对于像这样的小时间,第一个 运行 应该被删除并且至少应该执行几十个 运行s。我个人至少会使用数百个 运行s.

Do software engineers actually use these formal calculations in practice?

只有极少数开发人员进行高级统计分析。当 gab before/after 目标优化很大且组内变化很小时,通常不需要做一些非常正式的事情。

例如,如果您的程序比以前快两倍且 min-max 变化 <5%,那么您可以非常有把握地说优化是成功的。话虽这么说,但由于意外的外部因素有时并非如此(尽管差距如此之大的情况非常罕见)。

如果结果不明显,那么你需要做一些统计基础。您需要计算 标准偏差 平均时间和中位数时间 、删除第一个 运行、交错 运行s并使用许多 运行s(至少几十个)。由于阈值效应(例如缓存)的 central limit theorem. It is sometimes a mixture distribution,时间分布几乎总是遵循 正态分布 。如果您在时间上看到一些异常值,您可以绘制该值以便轻松查看。

如果存在阈值效应,则您需要应用高级统计分析,但这很复杂,而且通常不是预期的行为。 I 通常是基准测试有偏差的标志,无论如何在结果分析过程中都必须考虑错误或复杂的影响。因此,我强烈建议您在分析这种情况下的结果之前 fix/mitigate 问题。

假设时间服从正态分布,您可以只检查中位数是否接近均值,以及与均值之间的差距相比标准差是否较小。

一个更正式的方法是计算这些测试的 Student t-test and its associated p-value and check the significance of the p-value (eg. <5%). If there are more groups, An Anova can be used. If you are unsure about the distribution, you can apply non-parametric statistical tests like the Wilcoxon and Kruskal-Wallis tests (note that the statistical power 是不一样的)。在实践中,做这样的正式分析是 time-consuming 并且与简单的基本检查(使用均值和标准差)相比,它通常没有那么有用,除非你的修改影响了很多用户或者你打算写研究论文.

请记住,使用良好的统计分析并不能防止基准测试出现偏差。您需要尽量减少可能导致结果偏差的外部因素。一个常见的偏差是频率缩放:第一个基准可能比第二个更快,因为 turbo-boost 或者它可能更慢,因为处理器可能需要一些时间才能达到高频。缓存在基准偏差中也起着巨大的作用。还有许多其他因素会在实践中导致偏差,例如 compiler/runtime 版本、环境变量、配置文件、OS/driver 更新、内存对齐、OS 分页(尤其是在 NUMA 系统上)、硬件(例如热节流)、软件错误(通过分析奇怪的性能行为发现错误并不少见)等

因此,使基准测试尽可能 可重现 至关重要(通过修复版本和报告环境参数(以及可能 运行 基准测试在沙箱中,如果你是偏执狂并且它不会对时间造成太大影响)。像 Nix/Spack 这样的软件有助于打包,像 LXD 这样的容器,Docker 可以帮助建立一个更可重现的环境。

许多大型软件团队使用自动基准测试来检查性能回归的存在。工具可以为您正确地运行并定期进行统计分析。一个很好的例子是 Numpy 团队,他们使用一个名为 Airspeed Velocity 的包(参见 results)。 PyPy 团队还设计了自己的基准测试工具。 Linux 内核也有基准套件来检查回归(例如 PTS),许多专注于性能的公司都有这样的自动化基准测试工具(通常 home-made)。有许多现有的工具。

有关此主题的更多信息,请查看 Emery Berger 的精彩 Performance Matters 演示文稿。

经验法则

  1. 每个系列的最小值(!)=> 90ms vs 80ms
  2. 估计噪音=>~10ms
  3. 悲观主义 => 它可能并没有变慢。

还不开心吗?

  1. 进行更多测量。 (每个约 13 运行 秒)

  2. 交错 运行s。 (不要先测量 13x A 再测量 13x B。)

    理想情况下,您总是随机选择 运行 接下来是 A 还是 B(科学:随机试验),但这可能有点矫枉过正。任何错误来源都应该以相同的概率影响每个变体。 (比如 CPU 随着时间的推移逐渐升温,或者 运行 11 点之后开始的后台任务。)

  3. 返回步骤 1。

还不开心?是时候承认你已经 nerd-sniped。差异(如果存在)非常小,您甚至无法测量。选择更具可读性的变体并继续。 (或者,锁定您的 CPU 频率,隔离一个核心以进行测试,让您的系统安静下来...)

说明

  1. 最小值:很多人(和工具,甚至)取平均值,但最小值在统计上更稳定。您的基准测试在给定硬件上的速度有一个下限 运行,但没有上限,它可以被其他程序减慢多少。此外,取最小值会自动删除初始的“warm-up”运行.

  2. Noise:运用常识,只看一眼数字。如果您查看标准偏差,请使其看起来非常怀疑!一个异常值就会对其产生很大影响,以至于它几乎变得毫无用处。 (这通常不是正态分布。)

  3. 悲观主义:你发现这个优化真的很聪明,你真的想要优化版本更快!如果它看起来更好只是偶然,你会相信它。 (你知道的!)所以如果你在意正确,你必须对抗这种倾向。

免责声明

这些只是基本准则。 Worst-case 延迟在某些应用程序(流畅的动画或电机控制)中是相关的,但它会更难测量。优化在实践中无关紧要的东西很容易(而且很有趣!)。与其想知道你的 1% 的收益是否具有统计显着性,不如尝试其他方法。测量包括 OS 开销在内的完整程序。注释掉代码,或者 运行 工作两次,只是为了检查优化它是否值得。