以Skylake为例,其缓存行为64B。
我试图编写一个简单的程序来查看我可以消耗的最大内存带宽。在下面的代码中,我故意跨步64B,以便每个负载将获取不同的缓存行(64B)。我收集完成10M负载所用的时间,然后通过将负载数乘以64B来计算负载的内存。
然后,我启动同步广告的线程,并行运行以下代码。因此,当所有线程完成时,加载的总内存为总* NUM_OF_THREADS * 64B 。然后我将其除以(end_time-start_time)。
我得到的带宽比Skylake的理论峰值内存带宽高得多。因此,这是不正确的。但是我不知道我的计算出了什么问题。
我唯一能做出的猜测是,也许内存带宽未饱和,处理器会预取以下高速缓存行,以便许多负载实际上是从高速缓存加载的。但是由于我的内联汇编是密集的内存加载序列,所以我不确定如何确认我的猜测。
有何评论?谢谢。
st = start_timing()
do {
for (i=0; i< 10; i++) {
asm volatile("movl 0x0(%[P]),%[sum]\n\t"
"movl 0x40(%[P]),%[sum]\n\t"
"movl 0x80(%[P]),%[sum]\n\t"
"movl 0xc0(%[P]),%[sum]\n\t"
"movl 0x100(%[P]),%[sum]\n\t"
"movl 0x140(%[P]),%[sum]\n\t"
"movl 0x180(%[P]),%[sum]\n\t"
"movl 0x1c0(%[P]),%[sum]\n\t"
"movl 0x200(%[P]),%[sum]\n\t"
"movl 0x240(%[P]),%[sum]\n\t"
"movl 0x280(%[P]),%[sum]\n\t"
"movl 0x2c0(%[P]),%[sum]\n\t"
"movl 0x300(%[P]),%[sum]\n\t"
"movl 0x340(%[P]),%[sum]\n\t"
"movl 0x380(%[P]),%[sum]\n\t"
"movl 0x3c0(%[P]),%[sum]\n\t"
"movl 0x400(%[P]),%[sum]\n\t"
"movl 0x440(%[P]),%[sum]\n\t"
"movl 0x480(%[P]),%[sum]\n\t"
"movl 0x4c0(%[P]),%[sum]\n\t"
: [P]"+r"(p), [sum]"+r"(sum)
: );
}
total += 200;
p = q+ ((total%1000000)<<6);
} while (total < 10000000);
et = end_timing()
bw = (total * 64)/(et-st)
答案 0 :(得分:1)
是的,从每个缓存行加载dword是基准测试L1d以外的缓存的缓存/内存带宽的好方法。 (如果L1d中的数据仍然很热,则需要测量将数据通过装入执行单元存入寄存器的瓶颈;除非具有AVX512,否则它需要多条指令才能读取整个缓存行。)
可能是L1d或L2缓存命中。如果您从未写过该内存,那么如果它们在BSS中或使用malloc分配,则所有这些都将在写时复制映射到相同的物理零页。
或者简单地说,不同的内核具有自己的私有L1d缓存。参见How can cache be that fast?,了解有关electronic.SE的信息。但是,如果您实际上要使用10MB的物理RAM,那将比四核SKL台式机多。如果您的Skylake Xeon具有更多的L3缓存,那么,总带宽当然可以大大高于RAM。
此外,http://blog.stuffedcow.net/2013/01/ivb-cache-replacement/显示L3替换不是严格的伪LRU;它在最近的Intel中具有适应性,因此它可能比您希望从RAM循环中驱逐的能力更强。四核i7上的10MB可能足够小,足以获得L3命中率,而总L3则为8MB。
asm volatile
将阻止它进一步优化,并且应该可以使用"+r"(pointer)
输入来查看指针更新。编译器不会“知道” asm会读取指向的内存(因为您没有告诉过它,并且没有"memory"
破坏者),因此可以更早地将缓冲区中的任何早期存储优化为死商店。