如何通过IO时序测量找到L1缓存行大小的大小?

Jie*_*eng 36 c c++ performance caching cpu-architecture

作为一项学校作业,我需要找到一种方法来获取L1数据缓存行大小,而无需读取配置文件或使用api调用.假设使用内存访问读/写时序来分析和获取此信息.那我该怎么做呢?

在完成另一部分任务的不完整尝试中,为了找到缓存的级别和大小,我有:

for (i = 0; i < steps; i++) {
    arr[(i * 4) & lengthMod]++;
}
Run Code Online (Sandbox Code Playgroud)

我想也许我只需要改变第2行,(i * 4)部分?所以一旦我超过缓存行大小,我可能需要更换它,这需要一些时间?但它是如此直截了当?所需的块可能已经存在于内存中?或者perpahs我仍然可以依靠这样一个事实:如果我有足够大的steps,它仍然可以非常准确地运作?

UPDATE

下面是对GitHub的尝试 ...主要部分如下

// repeatedly access/modify data, varying the STRIDE
for (int s = 4; s <= MAX_STRIDE/sizeof(int); s*=2) {
    start = wall_clock_time();
    for (unsigned int k = 0; k < REPS; k++) {
        data[(k * s) & lengthMod]++;
    }
    end = wall_clock_time();
    timeTaken = ((float)(end - start))/1000000000;
    printf("%d, %1.2f \n", s * sizeof(int), timeTaken);
}
Run Code Online (Sandbox Code Playgroud)

问题是时间似乎没有太大差异.仅供参考.因为它用于L1缓存.我有SIZE = 32 K(数组的大小)

Ale*_*x D 27

分配一个BIG char阵列(确保它太大,不适合L1 L2缓存).用随机数据填写.

n字节为步骤开始遍历数组.对检索到的字节执行某些操作,例如对它们进行求和.

基准测试并计算您可以使用不同的值处理多少字节/秒n,从1开始并计数到1000左右.确保您的基准测试打印出计算的总和,因此编译器无法优化基准测试代码.

n==您的缓存行大小时,每次访问都需要在L1缓存中读取新行.因此,基准测试结果应该在这一点上变得非常急剧.

如果数组足够大,那么当你到达终点时,数组开头的数据将再次超出缓存,这就是你想要的.因此,在增加n并重新开始之后,结果将不会受到缓存中已有数据的影响.

  • 可能硬件预取将找出'n'的步骤,并将在您之前加载. (6认同)
  • 我认为这个想法应该有效,但是尝试以随机的方式采取n个步骤以避免预取,例如n +(r*c),其中c是比可能的高速缓存行大小大2的幂值并且r是随机值.你需要确保n +(r*c)可能在你的数组中使用modulo. (3认同)
  • 你的`data`数组只有32KB,所以整个东西都适合L1缓存.请注意我上面提到的第一件事:"分配一个BIG char数组.确保它太大而不适合L1或L2缓存." (2认同)

Ton*_*ion 5

您可以CPUID在汇编程序中使用该函数,虽然不可移植,但它会给您您想要的。

对于 Intel 微处理器,可以通过调用 cpuid 函数 0x1 后将 bh 乘以 8 来计算高速缓存线大小。

对于 AMD 微处理器,调用 cpuid 函数 0x80000005 后,数据 Cache Line Size 位于 cl 中,指令 Cache Line Size 位于 dl 中。

我从这里的这篇文章中摘取了这一点。


aus*_*len 5

看看Calibrator,所有的工作都受版权保护,但源代码是免费提供的.从它的文档想法来计算缓存行大小听起来比这里已经说过的更有教育意义.

我们的校准器工具的基本思想是拥有一个微基准测试,其性能仅取决于发生的高速缓存未命中的频率.我们的校准器是一个简单的C程序,主要是一个执行一百万次内存读取的小循环.通过改变步幅(即,两个后续存储器访问之间的偏移)和存储区域的大小,我们强制改变缓存未命中率.

原则上,高速缓存未命中的发生由阵列大小决定.一旦数据加载到缓存中,适合L1缓存的数组大小不会生成任何缓存未命中.类似地,超过L1高速缓存大小但仍然适合L2的阵列将导致L1未命中但没有L2未命中.最后,大于L2的阵列导致L1和L2未命中.

高速缓存未命中的频率取决于访问跨度和高速缓存行大小.当步幅等于或大于高速缓存行大小时,每次迭代都会发生高速缓存未命中.如果步幅小于高速缓存行大小,则仅每n次迭代(平均)发生高速缓存未命中,其中n是高速缓存行大小/步幅的比率.

因此,我们可以通过将没有未命中的执行时间与执行时间进行比较来计算高速缓存未命中的等待时间,每次迭代恰好一次丢失.这种方法只有在内存访问是纯顺序执行的情况下才有效,即我们必须确保两个或多个加载指令,内存访问和纯CPU工作都不能重叠.我们使用一个简单的指针追踪机制来实现这一点:我们访问的内存区域被初始化,以便每个加载在下一次迭代中返回后续加载的地址.因此,超标量CPU无法通过推测执行来隐藏内存访问延迟.

为了测量缓存特性,我们多次运行实验,改变步幅和数组大小.我们确保步幅至少在4个字节和最大预期高速缓存行大小的两倍之间变化,并且数组大小从最小预期高速缓存大小的一半变化到最大预期高速缓存大小的至少十倍.

我必须注释掉#include "math.h"它才能编译,之后它才能正确找到笔记本电脑的缓存值.我也无法查看生成的postscript文件.

  • 对于我的机器(Haswell),校准器不正确地预测线条尺寸,@ AlexD的方法也不起作用.问题是预取器,它设法猜测恒定步幅模式并欺骗实验.我想这可以通过禁用预取器来测量 (2认同)