(我的问题与计算机体系结构和性能理解有关。没有找到相关的论坛,因此将其作为一般问题发布在这里。)
我有一个C
程序,用于访问在虚拟地址空间中相隔X个字节的存储字。例如for (int i=0;<some stop condition>;i+=X){array[i]=4;}
。
我用变化的X
来衡量执行时间。有趣的是,当X
是2
的力量并且大约是页面大小时,例如X=1024,2048,4096,8192...
,我会发现性能大幅下降。但是,在X
的所有其他值上,例如1023
和1025
上,并没有减速。性能结果如下图所示。
我在多台个人计算机上测试了我的程序,所有这些计算机都在Intel CPU上运行带x86_64的Linux。
这种减速的原因可能是什么?我们已经尝试了在DRAM,L3缓存等中使用行缓冲区,但这似乎没有意义...
更新(7月11日)
我们在此处进行了一些测试,在原始代码中添加了NOP指令。而且减速仍然存在。否决了4k别名。冲突高速缓存未命中的原因更可能是这种情况。
答案 0 :(得分:1)
这里有两件事:
如果仅触摸1024的多个地址,则设置关联缓存别名会产生冲突缺失。内部快速缓存(L1和L2)通常由物理地址中的一小部分位索引。因此,跨度为1024字节意味着所有访问的地址位都是相同的,因此您只使用高速缓存中的一些地址集。
但是,如果使用非2的幂次方,您的访问将分布在缓存中的更多集合上。 Performance advantages of powers-of-2 sized data?(答案描述了这个 dis 优势)
Which cache mapping technique is used in intel core i7 processor?-共享的L3缓存可以抵抗较大的2乘幂偏移,因为它使用了更复杂的索引功能。
4k别名(例如在某些Intel CPU中)。尽管仅使用 存储,但这可能无关紧要。当CPU必须快速确定负载是否正在重新加载最近存储的数据时,这主要是造成内存歧义的因素,而在第一遍中,它仅通过查看页面偏移量位就可以做到。
这可能不是您要执行的操作,但有关更多详细信息,请参见:
L1 memory bandwidth: 50% drop in efficiency using addresses which differ by 4096+64 bytes和
Why are elementwise additions much faster in separate loops than in a combined loop?
这些影响中的一个或两个都可能是Why is there huge performance hit in 2048x2048 versus 2047x2047 array multiplication?
中的一个因素另一个可能的因素是,硬件预取在物理页面边界处停止。 Why does the speed of memcpy() drop dramatically every 4KB?但是,将步幅从1024更改为1023并不能在很大程度上帮助您实现这一目标。 IvyBridge和更高版本中的“下一页”预取仅是TLB预取,而不是下一页中的数据。
我大部分都假定使用x86,但是缓存别名/冲突遗漏的东西通常适用。具有简单索引的集关联高速缓存普遍用于L1d高速缓存。 (或者在较旧的CPU上,直接映射,其中每个“集合”只有1个成员)。 4k别名可能大部分是特定于Intel的。
跨虚拟页面边界进行预取可能也是一个普遍问题。