在优化软件时,我们通常会测量执行给定功能或任务所花费的时间。典型的假设是我们得到正态分布,因此我们应该报告平均时间。
我相信这个隐藏的假设(常态)在软件基准测试中经常被违反。我最近在“数据中心的基准测试”研讨会上发表了关于精确和准确的基准测试(视频、幻灯片)的演讲,我在会上详细阐述了这一点。
为什么这很重要?如果您有正态分布,您可以通过获取更多样本从数学上提高测量的准确性。您的错误应该下降为措施数量的平方根。
如果您曾试图增加样本数量以期获得更准确的结果,您可能会非常失望。那是因为时序通常更接近于对数正态分布:
对数正态分布是不对称的,你的意思是相对接近最小值,尾巴很长……随着你采集越来越多的样本,你可能会发现越来越大的值。
你经常可以证明你没有正态分布,因为你发现了 4-sigma、5-sigma 甚至 13-sigma 事件:你测量的值与你的估计标准偏差相比远高于平均值。在正态分布中,偏离均值的标准差是不可能的。但是,对于正常的日志,它更容易发生。
当然,真实数据是杂乱无章的。我并不是说您的时间安排完全遵循对数正态分布。它只是一个模型。然而,它表明报告平均值和标准误差是不够的。我喜欢测量最小值和平均值。然后,您可以使用平均值和最小值之间的距离作为易于测量的误差指标:如果平均值远离最小值,那么您的实际性能可能与最小值有很大差异。
最小值比平均值更容易准确测量。在实践中,我经常达到 1% 或更好的精度。也就是说,如果我改天在同一台机器上重新运行相同的基准测试,我几乎可以肯定结果的变化不会超过 1%。平均值稍微更难确定。你可以用一个模型来验证它是否如此:如果你生成对数正态分布的样本,你会发现更容易确定最小值而不是平均值。
在我的演讲中,我使用了一个受计算限制的例程:我的函数的性能不受 RAM、网络或磁盘速度的限制。我获取了 CPU 缓存中的数据,并在 CPU 缓存中生成了更多数据。对于这些类型的问题,我发现时序通常类似于对数正常。
其他类型的任务呢?内存限制任务呢?
我做了一个我喜欢用的内存基准测试。它由一个跨越数百兆字节的大型数组组成,软件必须在其中从一个位置跳到另一个位置,无法在完成读取之前预测下一个跳转。它通常被称为“指针追逐”例程。我可以交错指针追逐例程,这样我每次都有几个负载在飞行:我称之为车道数。我拥有的通道越多,我变得越“带宽受限”,通道越少,我变得越“内存延迟”。在这些测试中,我从未受到计算限制,这意味着每个周期退出的指令数总是很低。
对于每个固定数量的通道,我在运行 Ubuntu 22 的 Amazon Intel c6i 节点上运行基准测试 30 次。经过的时间在每次运行超过 3 秒(一条通道)到大约 1/6 秒(30 条通道)之间变化。然后我估计标准偏差,计算平均值、最大值和最小值。然后我将底部西格玛计算为最小值和平均值之间的差距(以标准差的数量表示),然后是平均值和最大值之间的差距。如果分布是正态分布,则两侧(最小值和最大值)的西格玛数量应该大致相同,并且不应超过 3 个西格玛。我发现一侧(最大值)很容易超过 3 西格玛,因此它不是正态分布。它也显然不对称。
但我的措施是相对精确的。最小值和平均值之间的相对距离,我得到一个很小的差值,通常低于 1%。
有趣的是,我拥有的通道越多,结果就越准确:从直觉上讲,这并不完全令人惊讶,因为它打破了数据依赖性,并且一个错误的步骤对整个处理的影响较小。
因此,对于范围广泛的与性能相关的时间,您不应该先检查就假设您具有正态分布!计算最大值和平均值之间的距离除以标准差是一个有用的指标。我个人发现对数正态分布是我计时的更好模型。
原文: https://lemire.me/blog/2023/04/06/are-your-memory-bound-benchmarking-timings-normally-distributed/