这段代码(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];
}
}
为什么?在水平或垂直方向上遍历图像中的所有像素是一样的。
有没有办法加速第二次?
提前致谢。
答案 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架构。有些比其他更敏感。有些人希望您触摸每个缓存行一次(最大容量),然后再进行复制。当然,时间和处理器共享会让事情变得混乱。