当我对一些代码进行基准测试时,无论是在一组固定的输入数据上,还是在随机输入上,其中随机性不会影响控制流:用于评估代码性能的最佳度量标准是什么?
我总是在多次运行中使用最小运行时间,因为任何与最小值的偏差都是由于CPU忙于不相关的事情,但我找不到任何可靠的来源确认这是最好的做法。其他明显的选择是平均或中位运行时间。 (最大似乎很奇怪,因为它可能会被无关的CPU峰值所控制。)有没有更好的方法来理解从多次运行中收集的统计数据?
正如paxdiablo指出的那样,如果我可以直接测量CPU时间,那将是理想的。但是,当我只能预测时间时,我该怎么办?
正如我所说,我无法找到任何可靠的信息,但也许我找不到合适的Google关键字,所以如果你能指出我现有的任何东西,那将会是一个很大的帮助。另外,如果这个问题对于SO来说过于笼统,请随意将其迁移到Programmers.SE。
答案 0 :(得分:3)
如果您对CPU时间进行基准测试,某些系统会为您提供独立于已用时间或墙壁时间的CPU使用率。
你是的,墙壁时间可能因系统可能会有所不同而有所不同,但这通常不会影响CPU时间。
举例来说,Linux(和其他类UNIX操作系统)中的time
实用程序报告如下:
pax> time sleep 1
real 0m1.001s
user 0m0.000s
sys 0m0.000s
实时是壁挂时间,触摸一秒钟。 user
和sys
时间是使用CPU所花费的时间,在这种情况下这是最小的,因为进程正在等待睡眠完成(一个几乎没有CPU时间的操作)。
如果你有这个设施,那就是你应该使用的设施。
如果您没有拥有这样的设施,那么您可能不得不使用统计方法,例如最大限度地减少其他流程的CPU使用率并运行您自己的流程数百个时间形成一个体面的画面。
您是采取平均值还是最小值(或者除去异常值后的平均值之类的奇怪值)将取决于您遵循的统计学派。如果您确定确定任何变体不是由于工作量本身造成的,那么您应该选择最低要求。
确保最小化其他负载非常重要。如果你有一个流氓进程占用了97%的CPU咕噜声,那么与大多数空闲系统相比,最低限度将会向上倾斜(这就是为什么CPU时间比壁挂时间好得多)。
答案 1 :(得分:1)
我一直在多次运行中使用最小运行时间,因为任何与最小运行时间的偏差都是由于 CPU 忙于处理无关的事情,但我找不到任何可靠的来源确认这是最佳实践。其他明显的选择是平均或中值运行时间。 (最大值看起来很奇怪,因为它可能会被无关的 CPU 峰值所支配。)有没有更好的方法来理解从多次运行中收集的统计数据?
Chen, J. and Revels, J., 2016. Robust benchmarking in noisy environments. arXiv preprint arXiv:1608.04295. 关于如何进行稳健的基准测试有一些适度广泛且在数学上合理的论点。 简单的版本是:使用最少。
正如您所料,因为它归结为:
噪音只会让事情运行得更慢,永远不会更快。
因此,minimum
而不是 mean
或 median
是真实时间的最佳估计。
由于模型是 true_time + noise
,其中 noise
是非负的,因此最小的样本必须具有最小的误差(因为 true_time
在样本之间不会改变,只有噪声)。>
答案 2 :(得分:1)
通常 min 和中值应该非常相似,除非它非常嘈杂。如果您假设其他负载实际上是嘈杂的而不是恒定的,并且您的基准测试不会太长以至于它平均掉了变化,那么检查它可能是一个很好的方法来判断系统对于基准测试来说不是太嘈杂。< /p>
有时(特别是对于微基准测试),即使在大部分空闲的系统上,您也会发现一个较低的异常值。如果我手动进行几次测试,我通常会忽略它并取可重复结果集群中的最低数字。如果系统非常嘈杂,中位数可能不在最低的集群中,但希望 是一组有点相似的快速结果。那就是你想要的;正如林登指出的那样,噪音(其他负载、中断、缓存污染)只会让事情变慢。
(虽然我认为如此低的异常值通常是真实的,比如为了避免分支混叠的事物对齐的一些巧合,或者大页面排列得很好,或者 IDK 什么。甚至可能是 CPU 指令调度的一些亚稳定平衡受到干扰并下降到较低吞吐量状态。同样,这是为了在大部分空闲系统上获得不可重复的良好性能。)
OTOH,根据您的基准测试,这可能没有意义。如果您的代码运行的实际条件将涉及内存带宽和缓存的竞争,和/或我/O,具有较差“最佳情况”但受噪声影响较小的代码总体上可能会更好。
例如你可能有一个非常快的算法,它真的取决于缓存中有 20MiB 的热内容。如果其他内核上的其他线程通常会“污染”缓存(包括来自云主机中同一硬件上的其他虚拟机),那么即使它偶尔运行得非常快,这实际上也不是最佳选择。
在最佳情况下使用更多 CPU 周期的代码(例如,通过压缩或重新计算数据,而不是巨大的查找表或未压缩的缓存)在平均现实世界条件下可能是最好的。
或者使用多线程或其他东西来解耦/流水线的代码,某些处理步骤可能有更多的开销并且在最好的情况下会更慢,但在一般情况下对噪声的抵抗力更强并且更好。
如果情况可能有所不同,请谨慎调整基准测试而不是实际系统条件。 并且通常尝试编写不会过度消耗共享资源(如内存带宽)的代码;例如对于大数组的数字运算,通过在适合单个内核的 L2 缓存的块上执行多个步骤来缓存块,因此您不会在每次遍历整个数据时从共享的 L3 或主内存中重新读取设置。