C ++代码的缓存优化

时间:2010-12-02 22:11:06

标签: c++ caching optimization openmp

这看起来似乎是开放式的,但我在为多个处理器和缓存优化一段C ++代码时遇到了麻烦。

比多处理器更重要的是缓存:我正在迭代2个嵌套循环

for(int i=0; i<n; i++){
  //do a little something here with a single array
  for(int j=0; j<whoaAnotherArray[n].size(); j++){
    * access array[i][j] and otherArray[i][j] and store in a variable
       - an example is: "int x = array[i][j] + otherArray[i][j]"
    * compare variable to some other array[index calculated from i and j]
       - an example is: "if (x < yetAnotherArray[i*n+j]){ //do something to yetAnotherArray }"
  }
}

我的数组(array和otherArray)的大小非常大。 n是他们的大小。

有没有办法让这个缓存更友好?我已经从使用链接列表切换,这对于缓存很糟糕。我在某处读到我的访问顺序[i] [j]也是缓存有效的。

FWIW,这是负重量循环检测算法的一部分。

我想也许是因为我的数组太大了(它们是btw的整数数组),最好将它们分解一下以便更好地适应缓存吗?但我不确定这是否正确,或者是否如此,如何去做。

我也开始使用openmp。我唯一要做的就是添加

#pragma omp parallel for
在for for循环之前,我正在获得不错的利用率。我想学习如何更好地使用并行性但超出我的代码中的循环我不知道我能做什么。并且一直以来:我正在尝试缓存友好。

3 个答案:

答案 0 :(得分:3)

缓存使用改进的一种可能性是修改您对arrayotherArray的访问模式。当您阅读array[i][j]时,您的机器当然会将一行“内存”移动到缓存中。当您阅读otherArray[i][j]时,您的机器当然会将一行“内存”移动到缓存中。读取第二个“行”可能需要将第一个从缓存刷新到RAM中。然后通过读取yetAnotherArray中的值,使情况更糟(可能)。

实际发生的事情很大程度上取决于同时发生了什么,缓存中还有什么以及正在执行的任何其他操作。这很难搞清楚。

如果你的(主导)数组访问模式同时需要来自两个(或所有3个)数组的element[i][j],那么你想安排一些事情,使它们在同一个“行”内存中这是读。一种方法是将3个数组合并为一个m*n*3数组,其中superArray[i][j][1]位于superArray[i][j][2]旁边,superArray[i][j][3]旁边的{{1}}每个阵列的平面代表一个原始阵列。当然,这只有在我有正确的索引顺序时才有效,所以请多考虑一下。

最后:

  1. 这可能会改变你的优雅 程序陷入意大利面乱 - 但是 这是一个很小的代价 提高速度!

  2. by'line'我的意思是什么大块     您的平台从RAM加载到     缓存一次。

  3. Google周围的循环平铺         和剥离挖掘。编译器是         还不是很擅长这个         以及你可以提供的任何帮助         获得改进执行奖励         速度。

答案 1 :(得分:3)

答案 2 :(得分:1)

有一个名为Cachegrind的程序(Valgrind插件)可以帮助您分析代码对虚拟缓存的执行情况。我将使用它来查看代码如何对付CPU的缓存。 (自从我使用它以来已经有一段时间了,所以我不记得它是否可以自动检测CPU的缓存属性。您可能需要为它提供CPU的确切缓存参数。)

您还可以尝试一些优化,理想情况下,您的编译器应该或应该这样做:

1)替换此行:

for(int j=0; j<whoaAnotherArray[n].size(); j++){

使用:

  int len = whoaAnotherArray[n].size();
  for(int j=0; j<len; j++){

2)创建指向外循环中数组的指针:

int* pArray = array[i] - 1;
int* pOtherArray = pOtherArray[j] - 1;

并在循环中的第一个指针访问中使用preincrements:

int x = *(++pArray) + *(++pOtherArray);

(是的,我知道那很丑。我知道编译器应该为你做这个。但是不是几个月前,我发现这确实是与linux上的gcc 4.3(?)的区别.YMMV。)

3)如果有任何方法可以重构代码,以便您在一次传递中循环array,然后在第二次传递中循环otherArray,那么尝试这样做。在你的情况下似乎不太可能,但我不知道。关键是,您希望一次将内存访问尽可能集中在一个数组上。

祝你好运。