我想测量C ++原子fetch_add在不同设置上所花费的时间。我写了这样的话:
atomic<uint64_t> x(0);
for (uint64_t i = 0; i < REPS; i+=1g) {
x.fetch_add(1);
}
因此,如果REPS
足够高,我认为可以平均每秒fetch_add
发生的事情。首先,我需要验证大部分时间确实花在fetch_add中,而不是例如循环开销。所以我跑了做这个。
这是来自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>
perf(对于循环事件)表示100%的周期进入sub $0x1,%eax
,而不是我期望的lock addl $0x1,0x20227c(%rip)
或跳跃。有什么想法吗?这是准确的,还是只是一个测量工件?在第二种情况下,为什么会系统地将延迟归因于sub
行而不是addl
?
答案 0 :(得分:3)
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);
}
使用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
当我将fetch的调用次数更改为5时,会识别出5个热点。这个结果表明,在这种情况下,存在一个系统的逐个指令错误归因于循环:
perf wiki包含以下warning:
&#34;基于中断的采样在现代处理器上引入了滑动。这意味着存储在每个样本中的指令指针指定程序被中断的位置以处理PMU中断,而不是计数器实际溢出的位置&#34;
&#34;如果有分支,这两个点之间的距离可能是几十个指令或更多。&#34;
所以,看起来我应该认为自己很幸运,因为注释被一个人拒绝了;)。
更新:英特尔处理器支持名为PEBS(基于精确事件的采样)的功能,该功能可将指针指针与计数器事件相关联,并且更不容易出错See this forum post。 / p>
对于选定的计数器,您也可以通过perf
访问此功能:
使用perf record -e cycles:pp
代替(注意:pp
后缀)这次注释的输出是:
: 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
这证实了预感。这是一个解决方案,可能对跳跃的棘手情况有所帮助。