我试图将一些性能工程技术应用于Dijkstra算法的实现。为了找到(天真的和未经优化的)程序的瓶颈,我使用perf
命令记录缓存未命中的数量。相关的代码片段如下,它找到距离最小的未访问节点:
for (int i = 0; i < count; i++) {
if (!visited[i]) {
if (tmp == -1 || dist[i] < dist[tmp]) {
tmp = i;
}
}
}
对于LLC-load-misses
指标,perf report
显示了程序集的以下注释:
│ for (int i = 0; i < count; i++) { ▒
1.19 │ ff: add $0x1,%eax ▒
0.03 │102: cmp 0x20(%rsp),%eax ▒
│ ↓ jge 135 ▒
│ if (!visited[i]) { ▒
0.07 │ movslq %eax,%rdx ▒
│ mov 0x18(%rsp),%rdi ◆
0.70 │ cmpb $0x0,(%rdi,%rdx,1) ▒
0.53 │ ↑ jne ff ▒
│ if (tmp == -1 || dist[i] < dist[tmp]) { ▒
0.07 │ cmp $0xffffffff,%r13d ▒
│ ↑ je fc ▒
0.96 │ mov 0x40(%rsp),%rcx ▒
0.08 │ movslq %r13d,%rsi ▒
│ movsd (%rcx,%rsi,8),%xmm0 ▒
0.13 │ ucomis (%rcx,%rdx,8),%xmm0 ▒
57.99 │ ↑ jbe ff ▒
│ tmp = i; ▒
│ mov %eax,%r13d ▒
│ ↑ jmp ff ▒
│ } ▒
│ } ▒
│ }
我的问题如下:为什么jbe
指令会产生如此多的缓存未命中?如果我没有弄错的话,该指令根本不必从内存中检索任何内容。我认为它可能与指令缓存未命中有关,但即使使用L1-dcache-load-misses
仅测量L1数据缓存未命中,也表明该指令中存在大量缓存未命中。
这让我有点不知所措。谁能解释这个(在我看来)奇怪的结果?提前谢谢。
答案 0 :(得分:0)
当您读取内存位置时,处理器将尝试预取相邻的内存位置并对其进行缓存。
如果你正在读取一个对象数组,这些对象都在连续的块中分配在内存中,那么效果很好。
但是,例如,如果你有一个存在于堆中的指针数组,那么除非你使用某种专门为此设计的自定义分配器,否则你不太可能在连续的内存部分上进行迭代。
因此,取消引用应被视为某种成本。结构数组可以更有效地指向结构的指针数组。
Herb Sutter(C ++委员会成员)在这次演讲中谈到这一点https://youtu.be/TJHgp1ugKGM?t=21m31s