2D阵列的两个方向之间的性能测试

时间:2011-10-07 14:42:25

标签: c++ image-processing

这段代码(A)执行得比第二段快得多(10倍):

for(int w=0; w<width; w++) {
        for(int h=1; h<height; h++) {
            image[h][w] = (1-a)*image[h][w] + a*image[h-1][w];
        }
    }

第二个:

for(int h=0; h<height; h++) {
        for(int w=1; w<width; w++) {
            image[h][w] = (1-a)*image[h][w] + a*image[h][w-1];
        }
    }

为什么?在水平或垂直方向上遍历图像中的所有像素是一样的。

有没有办法加速第二次?

提前致谢。

2 个答案:

答案 0 :(得分:8)

这与locality of reference有关。如果以与存储在内存中相同的顺序访问元素,这将比以跨步模式访问它们快得多,因为内存缓存和内存带宽将得到更有效的利用。

上面会解释第二个版本比第一个版本更快,这正是我的盒子上发生的事情:

aix@aix:~$ time ./ver1
real    0m29.421s

aix@aix:~$ time ./ver2
real    0m2.198s

以下是我用来分配数组的代码:

  double a = 0.5;
  int width = 2048;
  int height = 2048;
  double* data = new double[height * width];
  double** image = new double*[height];
  for (int i = 0; i < height; i++) {
    image[i] = data + i * width;
  }

版本1次以下循环:

  for (int iter = 0; iter < 100; iter++) {
    for(int w=0; w<width; w++) {
      for(int h=1; h<height; h++) {
        image[h][w] = (1-a)*image[h][w] + a*image[h-1][w];
      }
    }
  }

版本2循环:

  for (int iter = 0; iter < 100; iter++) {
    for(int h=0; h<height; h++) {
      for(int w=1; w<width; w++) {
        image[h][w] = (1-a)*image[h][w] + a*image[h][w-1];
      }
    }
  }

使用g++编译了-O3 4.4.3并在某个描述的Xeon框中运行(64位Ubuntu)。

如果你仍然100%确定你看到相反的效果,那么与我正在做的事情相比,你所做的事情肯定会有一些根本不同的东西。如果你告诉我们你的图像的尺寸以及它是如何分配的(为了帮助建立内存布局),它可能会有所帮助。

答案 1 :(得分:1)

aix对于参考地点是正确的。更明确一点,这是因为内存层次结构。

首次访问元素时,可能是缓存未命中。加载整个缓存行,然后发生读/写。

根据您遍历阵列的方向,下一次访问将位于位置i + 1或i + N. i + 1可能位于同一个缓存行中,但i + N通常位于另一个缓存行中,需要另一个大的提取。

对于小N来说,整个事情最终都在缓存中,并且与方向无关。对于适当大的N,并非所有阵列都可以适应缓存的最快(和最小)部分,因此包含元素i的缓存行可能在您访问i + M * N之前被丢弃,并且必须在访问i之前重新加载1。

为了尽可能快地完成它,您必须特别了解CPU架构。有些比其他更敏感。有些人希望您触摸每个缓存行一次(最大容量),然后再进行复制。当然,时间和处理器共享会让事情变得混乱。