"perf" - 计算每个方法的指令数

"perf" - count instructions per method

我想知道我代码中每个函数调用的动态指令计数,这样我就可以像这样查看该计数器:

name of function | instructions 
     foo()       |     3533 
     bar()       |     1234

所以下面的子问题:

  1. 这可以使用 perf 吗?
  2. 如果是:我应该使用哪种 perf 标志来获取(至少)这些信息?
  3. 如果否:我可以使用其他什么程序来做到这一点?

您是否要获取 static 指令计数,即每个函数在最终二进制文件中编译成的指令数?

如果是这样,这是二进制文件的静态 属性,因此您不需要 perf(在 运行 时间工作)来确定它 - 您可以用 objdump -d a.out 反汇编二进制文件并计算指令数。如果您想使其自动化,请使用您选择的脚本语言或 awk 或其他语言(可能寻找下一个空白行)。

举个例子,你可以这样:

int foo(int a, int b) {
    return a << (10 + b);
}

并且 objdump 输出看起来像 like this(您可以看到确切的内容取决于编译器和标志):

foo(int, int): # @foo(int, int)
  lea ecx, [rsi + 10]
  shl edi, cl
  mov eax, edi
  ret

所以一共4条指令,包括ret.

但是,也许您在谈论 动态 指令计数 - 即每个方法中总共 执行 的指令数在您的应用程序的特定 运行 中?在这种情况下,您可以使用 perf record -e instructions 后跟 perf report -n --stdio 快速获得近似答案,后者应列出函数及其样本数。您可以通过乘以总样本的比率和报告顶部显示的 "Event count" 来将相同的计数扩展到一个指令数。

典型的报告可能如下所示:

#
# Total Lost Samples: 0
#
# Samples: 51K of event 'instructions:p'
# Event count (approx.): 27502612549
#
# Overhead       Samples  Command      Shared Object        Symbol                                                                                           
# ........  ............  ...........  ...................  .................................................................................................
#
    22.01%          4824  uarch-bench  uarch-bench          [.] add_calibration
     1.92%          2480  uarch-bench  uarch-bench          [.] prefetcht2_bench2048_inner.top
     1.92%          2477  uarch-bench  uarch-bench          [.] prefetcht1_bench2048_inner.top
     1.91%           222  uarch-bench  uarch-bench          [.] prefetcht0_bench16_inner.top
     1.91%          2021  uarch-bench  uarch-bench          [.] load_loop512_inner.top

在合理的假设下,您可以预期这些统计结果与真实结果相当接近。然而,如果你想要一个精确的计数,解决方案是可用的,比如使用 Intel 的 Processor Trace,它可以重建一个进程的整个执行历史。 .

中也提到了这些

如果您想要精确计算每个块/每个函数的动态指令数,如果您使用的是 x86,Intel PIN(动态代码检测)之类的东西可能会更好。 (但我没用过,所以没法告诉你怎么用

或者可能使用 Intel PT 来跟踪分支(如果您使用的是 Intel CPU),结合基本块的静态指令计数,您可以获得廉价的动态指令计数。

gdb 可能有一种蛮力方法,每次单步执行并打印函数名称。 (或在每一步后打印指令指针)。