使用 perf-stat 获得准确的时间测量
Getting accurate time measurement with `perf-stat`
我尝试对用多种语言编写的简单 "hello, world" 程序进行基准测试。我在这个领域是 n00b,perf-stat
手册由于缺乏示例而无法轻松完成。
为此,我 运行 perf-stat
处于高优先级以避免上下文切换。所以我想出了类似的东西:
sudo chrt -f 99 perf stat -e cs -e cpu-clock ./hello_c
但是,我为同一程序获得的结果却大相径庭。例如,相同的 C 编译可执行文件的结果可以是:
0 cs # 0.000 K/sec
0.42 msec cpu-clock # 0.612 CPUs utilized
0.000694107 seconds time elapsed
0.000713000 seconds user
0.000000000 seconds sys
或
0 cs # 0.000 K/sec
0.58 msec cpu-clock # 0.620 CPUs utilized
0.000936635 seconds time elapsed
0.000000000 seconds user
0.000940000 seconds sys
在这个特定示例中,存在 0.242528 msec
不兼容,即使两个示例中的上下文切换都等于 0
。
有没有我遗漏的东西,我需要做一些计算?或者不可能得到更接近的结果?是否有任何其他选项可以解决此问题然后获取 n 次执行的平均值?
当您重复对看似相同的代码进行基准测试时,您会发现差异的原因有多种。我在 another answer 中介绍了一些原因,值得牢记。
但是,根据经验和概率,我们可以预先消除其中的许多。剩下的就是你短程序冷启动偏差比较大的最有可能的原因:
- CPU 省电和频率缩放功能。
- 实际的 运行 时间行为差异,即每次 运行 您的 运行 时间库、VM、OS 或其他支持基础设施中执行的不同代码程序。
- 某些缓存效果,或代码或数据对齐效果不同于 运行 运行。
您可以使用简单的 perf stat
将这三种效果分开,而无需覆盖事件列表,例如:
$ perf stat true
Performance counter stats for 'true':
0.258367 task-clock (msec) # 0.427 CPUs utilized
0 context-switches # 0.000 K/sec
0 cpu-migrations # 0.000 K/sec
41 page-faults # 0.159 M/sec
664,570 cycles # 2.572 GHz
486,817 instructions # 0.73 insn per cycle
92,503 branches # 358.029 M/sec
3,978 branch-misses # 4.30% of all branches
0.000605076 seconds time elapsed
首先查看 2.572 GHz
行。这显示了有效 CPU 频率,计算方法是将 CPU 周期的真实数量除以 task-clock
值(CPU 程序花费的时间)。如果这从 运行 运行 变化,挂钟时间性能偏差部分或完全由这种变化解释,最可能的原因是上面的 (1),即 CPU 频率缩放,包括缩放低于标称频率(省电)和高于标称频率(涡轮增压或类似功能)。
禁用频率缩放的细节取决于硬件,但适用于大多数现代 Linux 分布的常见方法是 cpupower -c all frequency-set -g performance
以抑制低于标称的缩放。
禁用涡轮增压更复杂,可能取决于硬件平台甚至特定 CPU,但对于最近的 x86,一些选项包括:
- 正在将
0
写入 /sys/devices/system/cpu/intel_pstate/no_turbo
(仅限英特尔)
- 为您系统中的每个
${core}
做一个 wrmsr -p${core} 0x1a0 0x4000850089
(尽管每个插槽上一个在 some/most/all 芯片上可能就足够了?)。 (仅限英特尔)
- 调整
/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
值以设置最大频率。
- 使用
userspace
调速器和/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed
设置固定频率。
另一种选择是简单地 运行 反复测试,并希望 CPU 快速达到稳定状态。 perf stat
通过 --repeat=N
选项内置支持:
-r, --repeat=<n>
repeat command and print average + stddev (max: 100). 0 means forever.
假设您观察到频率始终相同(在 1% 左右),或者您已经解决了频率问题但仍然存在一些差异。
接下来,检查 instructions
行。这是您的程序正在执行的总工作量的粗略指标。如果它与您的 运行 时间方差同向变化且相对方差相似,则您遇到类型 (2) 的问题:一些 运行 比其他人做更多的工作。不知道你的程序是什么,很难说更多,但你可以使用像 strace
、perf record + perf annotate
这样的工具来追踪它。
如果instructions
没有变化,频率是固定的,但是运行时间是变化的,你有类型(3)或"other"的问题。您需要查看更多的性能计数器,看看哪些与较慢的 运行s 相关:您是否有更多的缓存未命中?更多上下文切换?更多分支预测错误?清单还在继续。一旦你发现是什么让你变慢了,你就可以尝试隔离导致它的代码。您也可以转向另一个方向:使用传统的分析来确定代码的哪一部分在 运行s.
上变慢了
祝你好运!
我尝试对用多种语言编写的简单 "hello, world" 程序进行基准测试。我在这个领域是 n00b,perf-stat
手册由于缺乏示例而无法轻松完成。
为此,我 运行 perf-stat
处于高优先级以避免上下文切换。所以我想出了类似的东西:
sudo chrt -f 99 perf stat -e cs -e cpu-clock ./hello_c
但是,我为同一程序获得的结果却大相径庭。例如,相同的 C 编译可执行文件的结果可以是:
0 cs # 0.000 K/sec
0.42 msec cpu-clock # 0.612 CPUs utilized
0.000694107 seconds time elapsed
0.000713000 seconds user
0.000000000 seconds sys
或
0 cs # 0.000 K/sec
0.58 msec cpu-clock # 0.620 CPUs utilized
0.000936635 seconds time elapsed
0.000000000 seconds user
0.000940000 seconds sys
在这个特定示例中,存在 0.242528 msec
不兼容,即使两个示例中的上下文切换都等于 0
。
有没有我遗漏的东西,我需要做一些计算?或者不可能得到更接近的结果?是否有任何其他选项可以解决此问题然后获取 n 次执行的平均值?
当您重复对看似相同的代码进行基准测试时,您会发现差异的原因有多种。我在 another answer 中介绍了一些原因,值得牢记。
但是,根据经验和概率,我们可以预先消除其中的许多。剩下的就是你短程序冷启动偏差比较大的最有可能的原因:
- CPU 省电和频率缩放功能。
- 实际的 运行 时间行为差异,即每次 运行 您的 运行 时间库、VM、OS 或其他支持基础设施中执行的不同代码程序。
- 某些缓存效果,或代码或数据对齐效果不同于 运行 运行。
您可以使用简单的 perf stat
将这三种效果分开,而无需覆盖事件列表,例如:
$ perf stat true
Performance counter stats for 'true':
0.258367 task-clock (msec) # 0.427 CPUs utilized
0 context-switches # 0.000 K/sec
0 cpu-migrations # 0.000 K/sec
41 page-faults # 0.159 M/sec
664,570 cycles # 2.572 GHz
486,817 instructions # 0.73 insn per cycle
92,503 branches # 358.029 M/sec
3,978 branch-misses # 4.30% of all branches
0.000605076 seconds time elapsed
首先查看 2.572 GHz
行。这显示了有效 CPU 频率,计算方法是将 CPU 周期的真实数量除以 task-clock
值(CPU 程序花费的时间)。如果这从 运行 运行 变化,挂钟时间性能偏差部分或完全由这种变化解释,最可能的原因是上面的 (1),即 CPU 频率缩放,包括缩放低于标称频率(省电)和高于标称频率(涡轮增压或类似功能)。
禁用频率缩放的细节取决于硬件,但适用于大多数现代 Linux 分布的常见方法是 cpupower -c all frequency-set -g performance
以抑制低于标称的缩放。
禁用涡轮增压更复杂,可能取决于硬件平台甚至特定 CPU,但对于最近的 x86,一些选项包括:
- 正在将
0
写入/sys/devices/system/cpu/intel_pstate/no_turbo
(仅限英特尔) - 为您系统中的每个
${core}
做一个wrmsr -p${core} 0x1a0 0x4000850089
(尽管每个插槽上一个在 some/most/all 芯片上可能就足够了?)。 (仅限英特尔) - 调整
/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
值以设置最大频率。 - 使用
userspace
调速器和/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed
设置固定频率。
另一种选择是简单地 运行 反复测试,并希望 CPU 快速达到稳定状态。 perf stat
通过 --repeat=N
选项内置支持:
-r, --repeat=<n>
repeat command and print average + stddev (max: 100). 0 means forever.
假设您观察到频率始终相同(在 1% 左右),或者您已经解决了频率问题但仍然存在一些差异。
接下来,检查 instructions
行。这是您的程序正在执行的总工作量的粗略指标。如果它与您的 运行 时间方差同向变化且相对方差相似,则您遇到类型 (2) 的问题:一些 运行 比其他人做更多的工作。不知道你的程序是什么,很难说更多,但你可以使用像 strace
、perf record + perf annotate
这样的工具来追踪它。
如果instructions
没有变化,频率是固定的,但是运行时间是变化的,你有类型(3)或"other"的问题。您需要查看更多的性能计数器,看看哪些与较慢的 运行s 相关:您是否有更多的缓存未命中?更多上下文切换?更多分支预测错误?清单还在继续。一旦你发现是什么让你变慢了,你就可以尝试隔离导致它的代码。您也可以转向另一个方向:使用传统的分析来确定代码的哪一部分在 运行s.
祝你好运!