Raspberry PI基准计时与std::chrono::steady_clock的奇怪之处



我正试图使用std::chrono::steady_lock在Raspberry Pi 4上对一段DSP代码进行基准测试,但我得到的结果很奇怪。因为GNU评测工具在Raspberry Pi上不起作用,我一直在使用基准测试来评估代码优化,所以这是一件大事。

当同一测试在同一程序执行中多次运行时,是什么导致性能在基准程序执行之间相差10%,同时保持一致+/-1%?

约6秒基准测试的结果相差约10%。但奇怪的是,对于基准测试的特定执行,差异似乎是粘性的。每次运行程序时,我都会连续运行基准测试三次,得到大致相同的结果+/-1%。但是,当我重新运行该程序时,三个基准测试的结果与上一次运行的结果相差+/-10%,但新运行的三个结果都是+/-1%。

例如:

Run 1:
9:21:37. Performance: 0.0912333 x realtime
9:21:42. Performance: 0.0910667 x realtime
9:21:47. Performance: 0.0910667 x realtime
Run 2:
9:20:15. Performance: 0.106667 x realtime
9:20:21. Performance: 0.1062 x realtime
9:20:28. Performance: 0.106117 x realtime

每次跑步的结果大致在这两个极端之间随机变化。但这里的特殊之处在于,每次运行程序时执行的三个测试之间的结果一致性为+/-1%。

我是一个经验丰富的程序员,所以我知道基准测试会有所不同。但是,对于我正在尝试做的事情来说,大约10%的方差是不可行的。我也无法想出一个合理的理论来解释为什么方差会随着调用而变化。

测试中的代码是一种机器学习算法(LSTM->Dense),使用手工优化的霓虹灯内部函数来生成实时音频。大部分执行(~90%)是使用手工优化的氖气内部函数的矩阵和矢量运算。数据占用空间约为13kb(可轻松放入一级缓存)。代码占用空间未知,但可能不适合L1 i-cache。大多数代码管道都很漂亮,所以代码运行时可能会受到L1缓存带宽限制。到目前为止,优化已经使实时性从0.18倍提高到0.093倍。我认为可能还有15%的改进,但时间上的不准确在这一点上造成了阻碍。测试中的代码执行三次,实时性约为0.3倍,因此进一步的优化实际上是关键的。

已检查的内容:

  • 不是近地天体对齐问题。所有矩阵、矩阵行和向量都是16字节对齐的(在调试编译中使用断言进行检查)。

  • 不是CPU频率问题。CPU缩放调节器已设置为performance,并且所有CPU都以1.8Ghz运行

  • 我认为这与进程之间的缓存竞争无关。HTOP表示,当通过VNC连接时,空闲时CPU使用率约为6%,当通过ssh连接时,CPU使用率为0.3%(wifi供应商)。当通过SSH连接时,模式不会发生显著变化。

  • 我不认为它会随着代码运行在哪个CPU核心上而变化——尽管我只能使用HTOP来确定代码在特定运行中运行在哪个核心上,这并不是完全确定的。测试运行似乎偶尔会转移到不同的CPU核心,但在大多数情况下,它们似乎在每次执行运行的3个测试期间都在一个随机选择的核心上运行。

  • 我不认为这是热节流。CPU的温度只有47摄氏度。我认为复盆子PI 4s在温度达到80摄氏度之前不会加热。

  • 矢量运算依赖于GCC编译器的自动矢量化,它已经用restrict声明进行了适当的注释,并被验证为产生了最佳的neon矢量化(具有比我用neon内部函数产生的更好的指令调度)。

  • 不是计时器解决问题。对std::chrono::steady_clock::now()的连续调用产生37到56ns之间的增量。

  • 时钟的选择没有问题。steady_clock、system_clock和high_resolution_clock都表现出相同的行为。

验证的cpu频率:

$ cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor 
performance
performance
performance
performance
$ cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq 
1800000
1800000
1800000
1800000

我不知道你可能能帮助的事情:

  • 如何在Raspberry Pi上实现std::chrono::steady_clock。它是基于CPU时钟计数器的吗?如有任何细节,不胜感激。

  • 热节流是否反映在/sys/devices/system/cpu/cpuf*/cpufreq/scaleing_cur_freq中。我想是的,但我不确定。

  • 我显然错过了一些重要的东西。

技术细节:

  • 树莓派4b 8GB
  • Linux raspberrrypi 5.15.61-v8+#1579 SMP PREEMPT 2022年8月26日星期五英国夏令时11:16:44 aarch64 GNU/Linux
  • gcc版本10.2.1 20210110(Debian 10.2.1-6)
  • 测试在catch2测试框架下运行

最终确定了问题的根源。这个问题似乎是对一级缓存内容的非常温和的竞争,可能来自一些后台系统进程。

性能计数器表现出与基准测试相同的奇怪行为:每次启动测试程序时,连续3次运行显示基准测试结果的差异约为1%;但不同发射的结果相差约10%。

奇怪的是,测试运行之间的性能差异是一致的,并持续几秒钟。但是,考虑到一级缓存的干扰有多小,很难猜测一百多个正在运行的系统进程中有什么干扰了基准测试,以及为什么会出现这种相当不幸的模式,尤其是因为它们可能以任何调度器优先级运行。

性能计数器的测量结果说明了这个问题:对于具有2995条指令的函数,每次迭代平均有约30次额外的一级数据缓存未命中,这导致了基准测试结果的10%差异。惊人地

我无法猜测哪种系统进程会以在18秒内保持一致的速率污染L1数据缓存,但在更大的时间尺度上会有所不同。

好消息是:测试中的代码非常接近最优。(一个LSTM单元,具有两个实质性的乘法,以及大量的矢量化ArcTan和Sigmoid函数调用),它能够使用超过75%的可用缓存带宽,并且每个时钟周期发出几乎两条指令。呜呜!

测试数据

测试代码每次迭代的平均性能计数器测量值。该程序的每次启动都会运行约6秒的基准测试三次。

测试程序运行良好:

CpuClk      :           1,694
L1D Access  :           1,244
L1D Miss    :               6
L1I Miss    :               0
Instructions:           2,995
L2 Access   :              12
L2 Miss     :               0
---
CpuClk      :           1,694
L1D Access  :           1,244
L1D Miss    :               6
L1I Miss    :               0
Instructions:           2,995
L2 Access   :              12
L2 Miss     :               0
---
CpuClk      :           1,693
L1D Access  :           1,244
L1D Miss    :               6
L1I Miss    :               0
Instructions:           2,995
L2 Access   :              12
L2 Miss     :               0

糟糕的运行:

CpuClk      :           1,797
L1D Access  :           1,244
L1D Miss    :              37
L1I Miss    :               0
Instructions:           2,995
L2 Access   :              78
L2 Miss     :               0
---
CpuClk      :           1,794
L1D Access  :           1,244
L1D Miss    :              37
L1I Miss    :               0
Instructions:           2,995
L2 Access   :              78
L2 Miss     :               0
---
CpuClk      :           1,797
L1D Access  :           1,244
L1D Miss    :              37
L1I Miss    :               0
Instructions:           2,995
L2 Access   :              78
L2 Miss     :               0

最新更新