关于嵌套函数的性能最高结果

perf top result about nested functions

我们使用 perf top 来展示 CPU 的用法。结果显示两个函数

samples    pcnt    function
------     ----    ---------
...        ...     ....
12617.00   6.8%    func_outside
 8691.00   4.7%    func_inside
.....

其实这两个函数是这样嵌套的,而且总是1对1嵌套。

func_outside() {
  ....
  func_inside() 
  ... 
}

我是否应该得出结论,在 perf top 结果中,4.7% 实际上已经包含在 6.8% 中。如果排除 func_inside 的成本,func_outside 的成本是 2.1% (6.8-4.7)?

简答

没有报告的每个百分比仅针对该特定功能。所以 func_inside 个样本不计入 func_outside

详情

perf 的工作方式是定期收集性能样本。默认情况下 perf top 只是检查当前哪个函数 运行 然后将其添加到该函数的样本计数中。

我很确定是这种情况,但想验证 perf top 显示结果的方式,所以我编写了一个快速测试程序来测试其行为。这个程序有两个有趣的函数 outerinnerouter 函数在循环中调用 innerinner 所做的工作量由参数控制。编译时一定要使用 O0 以避免内联。命令行参数控制两个函数之间的工作比例。

运行参数./a.out 1 1 1000000000给出结果:

49.20%  a.out             [.] outer    
23.69%  a.out             [.] main    
21.32%  a.out             [.] inner    

运行参数./a.out 1 10 1000000000给出结果:

66.06%  a.out             [.] inner    
17.77%  a.out             [.] outer    
 9.50%  a.out             [.] main    

运行参数./a.out 1 100 1000000000给出结果:

88.53%  a.out             [.] inner    
 2.85%  a.out             [.] outer    
 1.09%  a.out             [.] main    

如果 inner 的计数包含在 outer 中,则 outer 的运行时间百分比将始终高于 inner。但正如这些结果所表明的那样,情况并非如此。

下面是我用的测试程序,是用gcc -O0 -g --std=c11 test.c.

编译的
#include <stdlib.h>
#include <stdio.h>

long inner(int count) {
  long sum = 0;
  for(int i = 0; i < count; i++) {
    sum += i;
  }
  return sum;

}

long outer(int count_out, int count_in) {
  long sum = 0;
  for(int i = 0; i < count_out; i++) {
    sum += inner(count_in);
  }
  return sum;
}

int main(int argc, char **argv)  {
  if(argc < 4) {
    printf("Usage: %s <outer_cnt> <inner_cnt> <loop>\n",argv[0]);
    exit(-1);
  }

  int outer_cnt = atoi(argv[1]);
  int inner_cnt = atoi(argv[2]);
  int loops     = atoi(argv[3]);

  long res = 0;
  for(int i = 0; i < loops; i++) {
    res += outer(outer_cnt, inner_cnt);
  }

  printf("res is %ld\n", res);
  return 0;
}