使用跨步流访问测量峰值内存带宽有什么问题



以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]nt"
"movl 0x40(%[P]),%[sum]nt"
"movl 0x80(%[P]),%[sum]nt"
"movl 0xc0(%[P]),%[sum]nt"
"movl 0x100(%[P]),%[sum]nt"
"movl 0x140(%[P]),%[sum]nt"
"movl 0x180(%[P]),%[sum]nt"
"movl 0x1c0(%[P]),%[sum]nt"
"movl 0x200(%[P]),%[sum]nt"
"movl 0x240(%[P]),%[sum]nt"
"movl 0x280(%[P]),%[sum]nt"
"movl 0x2c0(%[P]),%[sum]nt"
"movl 0x300(%[P]),%[sum]nt"
"movl 0x340(%[P]),%[sum]nt"
"movl 0x380(%[P]),%[sum]nt"
"movl 0x3c0(%[P]),%[sum]nt"
"movl 0x400(%[P]),%[sum]nt"
"movl 0x440(%[P]),%[sum]nt"
"movl 0x480(%[P]),%[sum]nt"
"movl 0x4c0(%[P]),%[sum]nt"
: [P]"+r"(p), [sum]"+r"(sum)
: );
}   
total += 200;
p = q+ ((total%1000000)<<6);
} while (total < 10000000);
et = end_timing()
bw = (total * 64)/(et-st)

是的,从每个缓存行加载 dword 是基准测试除 L1d 以外的缓存的缓存/内存带宽的好方法。 (如果数据在L1d中保持热,则需要测量通过负载执行单元将其导入寄存器的瓶颈; 除非您有 AVX512,否则读取整个缓存行需要多条指令。

可能您正在获得 L1d 或 L2 缓存命中。 如果您从未写入内存,则如果内存在 BSS 中或使用 malloc 分配,则所有内存都将在写入时复制映射到同一物理零页。

或者只是不同的内核有自己的私有 L1d 缓存。 请参阅缓存如何如此之快?electronics.SE 那边。 但是,如果您实际接触10MB的物理RAM,则比四核SKL台式机还要多。 如果您的Skylake Xeon具有更多L3缓存,那么是的,聚合带宽当然可以明显高于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),所以任何早期存储到缓冲区中都可以优化为死存储。

最新更新