以Skylake为例,其缓存行为64B。
我试着编写一个简单的程序来查看我可以消耗的峰值内存带宽是多少。在下面的代码中,我故意制作步幅64B,以便每个加载将获取不同的缓存行(64B)。我收集用于完成10M负载的时间,然后通过将负载数乘以64B来计算加载的内存。
然后我启动线程,同步广告运行下面的代码并行。因此,当所有线程完成时,加载的总内存总数为* NUM_OF_THREADS * 64B。然后我将其除以(end_time-start_time)。
我获得的带宽远远高于Skylake的理论峰值内存带宽。所以这是不正确的。但我不知道我的计算有什么问题。
我可以做的唯一猜测是,可能内存BW未饱和,处理器预取以下缓存行,以便许多负载实际从缓存加载。但由于我的内联汇编是一系列密集的内存负载,我不知道如何确认我的猜测。
任何意见?谢谢。
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)
是的,来自每个缓存行的双字加载是对除L1d之外的缓存的缓存/内存带宽进行基准测试的好方法。 (如果数据在L1d中保持热点,则需要测量将其通过加载执行单元送入寄存器的瓶颈;除非您使用AVX512,否则需要多条指令才能读取整个缓存行。)
可能你正在获得L1d或L2缓存命中。如果你从未写过内存,那么如果它在BSS中或者用malloc分配的话,它们都将被写入映射到同一物理零页面的copy-on-write。
或者只是不同的核心有自己的私有L1d缓存。请参阅电子设备上的How can cache be that fast?。但是,如果您实际接触10MB物理RAM,那么这不仅仅是四核SKL桌面。如果你有一个具有更多L3缓存的Skylake Xeon,那么总带宽当然可以显着高于RAM。
另外,http://blog.stuffedcow.net/2013/01/ivb-cache-replacement/表明L3替换不是严格的伪LRU;它在最近的英特尔中是自适应的,因此它可能比你期望从循环RAM上驱逐更具抵抗力。 10MB可能足够小,可以在四核i7上获得一些L3点击,总L3为8MB。
asm volatile
将阻止它进行优化,并且"+r"(pointer)
输入应该可以看到你的指针更新。编译器不“知道”asm读取指向内存(因为你没有告诉它,并且没有"memory"
clobber),因此缓冲区中的任何早期存储都可以作为死存储进行优化。