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

Memory 使用跨步流访问来测量峰值内存带宽有什么问题,memory,benchmarking,cpu-architecture,microbenchmark,Memory,Benchmarking,Cpu Architecture,Microbenchmark,以Skylake为例,它的缓存线是64B 我试着写一个简单的程序,看看我能消耗多少峰值内存带宽。在下面的代码中,我特意将跨步设置为64B,以便每个加载都会获取不同的缓存线(64B)。我收集完成10M加载所用的时间,然后将加载的数量乘以64B来计算加载的内存 然后我启动同步ad的线程,并行运行下面的代码。因此,当所有线程完成时,加载的总内存是total*NUM\u OF_threads*64B。然后我将其除以(结束时间-开始时间) 我得到的带宽比Skylake的理论峰值内存带宽高得多。所以这是不正

以Skylake为例,它的缓存线是64B

我试着写一个简单的程序,看看我能消耗多少峰值内存带宽。在下面的代码中,我特意将跨步设置为64B,以便每个加载都会获取不同的缓存线(64B)。我收集完成10M加载所用的时间,然后将加载的数量乘以64B来计算加载的内存

然后我启动同步ad的线程,并行运行下面的代码。因此,当所有线程完成时,加载的总内存是total*NUM\u OF_threads*64B。然后我将其除以(结束时间-开始时间)

我得到的带宽比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)
st=开始计时()
做{
对于(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)
: );
}   
总数+=200;

p=q+((总计%1000000)是的,从每个缓存线加载dword是为L1d以外的缓存测试缓存/内存带宽的好方法。(如果数据在L1d中保持热状态,则需要测量将其通过加载执行单元加载到寄存器的瓶颈;除非有AVX512,否则读取整个缓存线需要多条指令。)

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

或者简单地说,不同的内核都有自己的专用L1d缓存。请参阅electronics.SE。但是,如果您实际使用的是10MB物理RAM,这比四核SKL桌面所拥有的还要多。如果您有一个具有更多L3缓存的Skylake Xeon,那么是的,聚合带宽当然会明显高于RAM

此外,还表明L3替换并不是严格意义上的伪LRU;它在最近的Intel中是自适应的,因此它可能比您预期的更具抵抗力,无法通过RAM循环。10MB可能足够小,可以在四核i7上获得一些L3命中率,总共8MB的L3



asm volatile
将阻止它进行优化,并且
“+r”(指针)
输入应该可以看到指针更新。编译器不“知道”asm读取指向内存(因为您没有告诉它,并且没有
“内存”
clobber),所以缓冲区中的任何早期存储都可以作为死区存储进行优化。

您得到的实际数字是多少?使用
perf stat
计算缓存未命中率时,您看到了什么?我使用perf测量实际内存带宽消耗约为95GB/s。但我在原始帖子中提到的计算结果超过600GB/s:)您可以使用性能事件
MEM\u LOAD\u RETIRED.L1\u HIT
MEM\u LOAD\u RETIRED.L2\u HIT
,以及
MEM\u LOAD\u RETIRED.L3\u HIT
,来测量每一级别的缓存命中数。我猜您得到了许多一级命中,但如果不看到整个代码,很难说出原因。您所说的是什么“我使用perf来测量实际的内存带宽消耗,大约为95GB/s”?比如如何测量?我仔细检查了最终的程序集,并且我的内联程序集保持不变。我没有真正初始化(或存储)数组('q'))既然你能看到我只是一步一步地读了一遍。难道我不认为我接触到了10M*64B=640BM的内存是因为跨步访问吗?@yeeha:哦,我没有足够仔细地阅读这个问题。我错过了10M的加载,而不是10MB的缓冲区大小。但我认为不初始化数组可以解释你的观察结果。你会错过TLB但是,如果所有页面都映射到相同的物理页面,则数据缓存会命中。