在迭代简单的2D阵列时提高缓存性能?

时间:2012-11-27 21:32:04

标签: java c caching

我一直在尝试一种方法来重写下面的代码,以提高数组中的缓存性能(通过减少缓存中的未命中)。

我知道数组是逐行(顺序)存储在内存中的,所以ary [0] [0],ary [0] [1],ary [0] [2],.... ary [1] [0],ary [1] [1],ary [1] [2] ... ary [50] [0],ary [50] [1] ... ary [50] [50] 。但是,我不确定如何使用此信息来帮助我弄清楚如何修改循环以提高缓存性能。

for (c = 0; c < 50; c++)
    for (d = 0; d < 50; d++)
        ary[d][c] = ary[d][c] + 1;

4 个答案:

答案 0 :(得分:4)

如果要一次访问行的所有单元格,只需反转两个循环:

for (d = 0; d < 50; d++)
    for (c = 0; c < 50; c++)
        ary[d][c] = ary[d][c] + 1;

甚至

for (d = 0; d < 50; d++)
    int[] array = ary[d];
    for (c = 0; c < 50; c++)
        array[c] = array[c] + 1;

但我怀疑它有任何重大影响,甚至任何影响,特别是在这么小的阵列上。使您的代码简单易读。不要预先优化。

答案 1 :(得分:3)

交换循环顺序。您在arr[1][0]之后立即访问arr[0][0]arr[1][0]距离更远,而arr[0][1]位于下一个地址。

答案 2 :(得分:1)

您希望最大限度地减少缓存未命中数以提高性能。每次高速缓存未命中都会导致内存访问并将新块加载到高速缓存。此块不仅包含您需要的值,还包含内存中的其他相邻值。您需要使用locality原则,即尽可能多地使用每个内存访问的值。就像你在观察中提到的那样,数组在内存中逐行存储,因此以顺序方式遍历数组将最大限度地减少缓存未命中数。回到你的代码, 要么交换循环顺序:

for (d = 0; d < 50; d++)
    for (c = 0; c < 50; c++)
        ary[d][c] = ary[d][c] + 1;

或交换计算中的指数:

for (c = 0; c < 50; c++)
    for (d = 0; d < 50; d++)
        ary[c][d] = ary[c][d] + 1;

您甚至可以将2D数组视为50 * 50大小的一维数组,只需使用一个for循环就可以从头到尾扫描它。

答案 3 :(得分:0)

除了交换循环之外,您可能不需要做任何事情,因为缓存旨在自己利用代码中的引用局部性,这意味着它将缓存第一个元素以及以下几个元素(数组中的空间局部性,并将它们保留在缓存中一段时间​​(时间局部性)。

但是,有些编译器允许您控制缓存,例如gcc具有__builtin_prefetch,它允许您控制应该预取哪些数据以及是否应将其保留在缓存中。

  

- 内置函数:void __builtin_prefetch(const void * addr,rw,locality)

     

此函数用于通过在访问数据之前将数据移入缓存来最小化缓存未命中延迟。你可以插入电话   __builtin_prefetch为代码,您可以在其中了解可能很快访问的内存中的数据地址。如果目标支持   它们,生成数据预取指令。如果预取是   在访问之前足够早地完成,然后数据将在缓存中   到访问时。

手册给出了这个例子:

for (i = 0; i < n; i++)
{
  a[i] = a[i] + b[i];
  __builtin_prefetch (&a[i+j], 1, 1);
  __builtin_prefetch (&b[i+j], 0, 1);
  /* ... */
}