为了分析执行时间的某些属性,我打算在程序的单独执行中同时使用Perf和PIN来获取我的所有信息。 PIN会给我指令混音,Perf会给我这些混音的硬件性能。作为一个完整性检查,我分析了以下命令行参数:
g++ hello_world.cpp -o hello
所以我的完整命令行输入如下:
perf stat -e cycles -e instructions g++ hello_world.cpp -o hello
pin -t icount.so -- g++ hello_world.cpp -o hello
在PIN命令中,为了这篇文章,我忽略了文件的所有路径内容。此外,除了默认的动态指令计数之外,我还更改了基本icount.so
以记录指令混音。结果令人惊讶地不同
PIN Results:
Count 1180608
14->COND_BR: 295371
49->UNCOND_BR: 21869
//skipping all of the other instruction types for now
Perf Results:
20,538,346 branches
105,662,160 instructions # 0.00 insns per cycle
0.072352035 seconds time elapsed
这应该通过具有大致相同的指令计数和大致相同的分支分布来作为健全性检查。 为什么动态指令计数会被 x100 的因子关闭?!我曾经有过一些噪音,但这有点多了。
此外,Perf的分支数量为20%,但PIN报告约为25%(这似乎也有点差异,但它可能只是大量指令计数失真的副作用)。
答案 0 :(得分:1)
icount
pintool 计算的内容与 instructions
性能事件之间存在显着差异,后者映射到现代英特尔处理器上的架构 Instructions Retired
硬件性能事件。我假设您使用的是 Intel 处理器。
pin
仅在指定 -follow_execv
命令行选项时注入子进程,如果 pintool 注册了回调函数来拦截进程创建,则回调返回 true
。另一方面,perf
默认配置所有子进程。您可以使用 perf
选项告诉 -i
仅分析指定的进程。perf
,默认情况下,分析在用户模式和内核模式下发生的所有事件(如果 /proc/sys/kernel/perf_event_paranoid
小于 2)。 pin
仅支持在用户模式下进行分析。icount
pintool 以基本块粒度计数,它本质上是一个短的、单入口、单出口的指令序列。如果块中的指令导致异常,则块中的其余指令将不会被执行,但它们已经被计数。可以在不终止程序的情况下处理异常。 instructions
只计算退休时的指示。icount
pintool 将 rep
前缀指令的每次迭代计为一条指令。 instructions
事件将带有 rep
前缀的指令计为一条指令,而不考虑迭代次数。instructions
事件可能会多计数或少计数。由于前两个原因,instructions
事件计数可能更大。由于接下来的两个原因,icount
pintool 指令计数可能更大。最后一个原因可能会导致不可预测的差异。由于 perf
计数大约是 icount
计数的 100 倍,因此很明显前两个因素在这种情况下占主导地位。
您可以通过将 -i
传递给 perf
来不分析孩子,将 :u
修饰符添加到 instructions
事件名称,从而获得两个工具来获得更接近的计数仅在用户模式下计数,并将 -reps 1
传递给 pin
以计算每条指令而不是每次迭代的 rep
前缀指令。
perf stat -i -e cycles,instructions:u g++ hello_world.cpp -o hello
pin -t icount.so -reps 1 -- g++ hello_world.cpp -o hello
您可以将 -i
传递给 perf
,而不是将 -follow_execv
传递给 pin
,如下所示:
pin -follow_execv -t icount.so -reps 1 -- g++ hello_world.cpp -o hello
通过这种方式,两个工具都将分析以指定进程(即正在运行的 g++
)为根的整个进程层次结构。
我希望这些措施的计数非常接近,但它们仍然不会完全相同。