orm*_*orm 5 c++ x86 c++11 perf
我想测量 C++ 原子 fetch_add 在不同设置下花费的时间。我写了这样的东西:
atomic<uint64_t> x(0);
for (uint64_t i = 0; i < REPS; i+=1g) {
x.fetch_add(1);
}
Run Code Online (Sandbox Code Playgroud)
因此,如果REPS足够高,我认为能够对fetch_add发生的情况。首先,我需要验证大部分时间确实花费在 fetch_add 中,而不是循环开销等。所以我运行 perf 来做到这一点。
这是来自 objdump 的程序集:
400ed0: b8 00 b4 c4 04 mov $0x4c4b400,%eax
400ed5: 0f 1f 00 nopl (%rax)
400ed8: f0 83 05 7c 22 20 00 lock addl $0x1,0x20227c(%rip)
400edf: 01
400ee0: 83 e8 01 sub $0x1,%eax
400ee3: 75 f3 jne 400ed8 <_Z10incrsharedv+0x8>
Run Code Online (Sandbox Code Playgroud)
perf (对于周期事件)表示 100% 的周期进入,这与我所期望的或跳跃sub $0x1,%eax相反。lock addl $0x1,0x20227c(%rip)有什么想法吗?这是准确的,还是只是一个测量工件?在第二种情况下,为什么 perf 会系统地将延迟归因于线路sub而不是addl?
TL;DR:尝试使用:pp后缀,对于某些事件处理器可以帮助您提供更准确的注释数据。
更长的版本:
在尝试调查我所描述的行为时,我还尝试使用以下更展开的循环。我认为这在一定程度上解决了这个问题。
for (uint64_t i = 0; i < REPS; i+=10) {
x.fetch_add(1, ORDER);
x.fetch_add(1, ORDER);
x.fetch_add(1, ORDER);
x.fetch_add(1, ORDER);
x.fetch_add(1, ORDER);
x.fetch_add(1, ORDER);
x.fetch_add(1, ORDER);
x.fetch_add(1, ORDER);
x.fetch_add(1, ORDER);
x.fetch_add(1, ORDER);
}
Run Code Online (Sandbox Code Playgroud)
使用时perf record -e cycles
生成的性能注释是:
: 0000000000400f00 <incr(std::atomic<unsigned long>&)>:
0.00 : 400f00: mov $0x3d0900,%eax
0.00 : 400f05: nopl (%rax)
0.00 : 400f08: lock addq $0x1,(%rdi)
10.93 : 400f0d: lock addq $0x1,(%rdi)
9.77 : 400f12: lock addq $0x1,(%rdi)
10.22 : 400f17: lock addq $0x1,(%rdi)
8.97 : 400f1c: lock addq $0x1,(%rdi)
10.39 : 400f21: lock addq $0x1,(%rdi)
9.87 : 400f26: lock addq $0x1,(%rdi)
10.48 : 400f2b: lock addq $0x1,(%rdi)
9.70 : 400f30: lock addq $0x1,(%rdi)
10.19 : 400f35: lock addq $0x1,(%rdi)
9.49 : 400f3a: sub $0x1,%rax
0.00 : 400f3e: jne
Run Code Online (Sandbox Code Playgroud)
当我将 fetch add 的调用次数更改为 5 时,识别出 5 个热点。这一结果表明,在这种情况下,在归因周期时存在系统性的相差一指令错误:
perf wiki 包含以下警告:
“基于中断的采样在现代处理器上引入了打滑现象。这意味着存储在每个样本中的指令指针指定了程序被中断以处理 PMU 中断的位置,而不是计数器实际溢出的位置”
“如果有分支的话,这两点之间的距离可能是几十条指令,甚至更长。”
所以,看来我应该认为自己很幸运,因为注释少了一个;)。
更新: 英特尔处理器支持称为 PEBS(基于精确事件的采样)的功能,该功能使得将指令指针与计数器事件关联起来更不容易出错, 请参阅此论坛帖子。
perf对于选定的计数器,您也可以通过以下方式访问此功能:
改用perf record -e cycles:pp(注意:pp后缀),这次 annotate 的输出是:
: 0000000000400f00 <incr(std::atomic<unsigned long>&)>:
0.00 : 400f00: mov $0x3d0900,%eax
0.00 : 400f05: nopl (%rax)
10.75 : 400f08: lock addq $0x1,(%rdi)
10.15 : 400f0d: lock addq $0x1,(%rdi)
10.00 : 400f12: lock addq $0x1,(%rdi)
9.22 : 400f17: lock addq $0x1,(%rdi)
10.21 : 400f1c: lock addq $0x1,(%rdi)
9.75 : 400f21: lock addq $0x1,(%rdi)
9.95 : 400f26: lock addq $0x1,(%rdi)
10.02 : 400f2b: lock addq $0x1,(%rdi)
10.18 : 400f30: lock addq $0x1,(%rdi)
9.75 : 400f35: lock addq $0x1,(%rdi)
0.00 : 400f3a: sub $0x1,%rax
0.00 : 400f3e: jne 400f08
Run Code Online (Sandbox Code Playgroud)
这证实了预感。这是一个在更棘手的跳跃情况下可能会有所帮助的解决方案。
| 归档时间: |
|
| 查看次数: |
1934 次 |
| 最近记录: |