使用perf-stat获得准确的时间测量

时间:2019-04-28 23:47:22

标签: linux-kernel benchmarking scheduler perf

我尝试对以几种语言编写的简单“ hello,world”程序进行基准测试。我是该领域的专家,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次执行吗?

1 个答案:

答案 0 :(得分:3)

当您重复基准相同的代码时,有多种原因会导致您看到变化。我已经在another answer中介绍了一些原因,值得牢记这些。

但是,基于经验和概率,我们可以预先消除很多。剩下的是最可能的原因是冷启动时您的短程序偏差相对较大:

  1. CPU省电和频率缩放功能。
  2. 实际的运行时行为差异,即,每次运行程序时,都会在运行时库,VM,OS或其他支持基础结构中执行的代码不同。
  3. 某些缓存效果,或者代码或数据对齐效果因运行而异。

您可以用普通的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(仅Intel)
  • 为系统中的每个wrmsr -p${core} 0x1a0 0x4000850089做一个${core}(尽管在某些/大多数/所有芯片上,每个插槽一个都足够了?)。 (仅限英特尔)
  • 调整/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)的问题:某些运行比其他运行要做的工作更多。不知道您的程序是什么,很难说更多,但是您可以使用straceperf record + perf annotate之类的工具进行跟踪。

如果instructions不变,并且频率固定,但是运行时间不同,则可能是类型(3)或“其他”的问题。您将需要查看更多的性能计数器,以了解哪些与运行速度较慢有关:您是否有更多的缓存未命中?更多上下文切换?更多分支预测错误?清单继续。一旦找出导致速度下降的原因,就可以尝试隔离导致它的代码。您还可以朝另一个方向发展:使用传统的性能分析来确定缓慢运行时代码的哪一部分变慢。

祝你好运!