745 c++ performance gcc memory-management
以下是相关程序的摘录.矩阵img[][]的大小为SIZE×SIZE,并在以下位置初始化:
img[j][i] = 2 * j + i
然后,你创建一个矩阵res[][],这里的每个字段都是img矩阵中它周围9个字段的平均值.为简单起见,边框保留为0.
for(i=1;i<SIZE-1;i++)
for(j=1;j<SIZE-1;j++) {
res[j][i]=0;
for(k=-1;k<2;k++)
for(l=-1;l<2;l++)
res[j][i] += img[j+l][i+k];
res[j][i] /= 9;
}
Run Code Online (Sandbox Code Playgroud)
这就是该计划的全部内容.为了完整起见,以下是之前的内容.没有代码.如您所见,它只是初始化.
#define SIZE 8192
float img[SIZE][SIZE]; // input image
float res[SIZE][SIZE]; //result of mean filter
int i,j,k,l;
for(i=0;i<SIZE;i++)
for(j=0;j<SIZE;j++)
img[j][i] = (2*j+i)%8196;
Run Code Online (Sandbox Code Playgroud)
基本上,当SIZE是2048的倍数时,此程序很慢,例如执行时间:
SIZE = 8191: 3.44 secs
SIZE = 8192: 7.20 secs
SIZE = 8193: 3.18 secs
Run Code Online (Sandbox Code Playgroud)
编译器是GCC.据我所知,这是因为内存管理,但我对这个主题并不太了解,这就是我在这里问的原因.
另外如何解决这个问题会很好,但如果有人能够解释这些执行时间,我已经足够开心了.
我已经知道malloc/free了,但问题不在于使用的内存量,它只是执行时间,所以我不知道这会有多大帮助.
Mys*_*ial 942
差异是由以下相关问题引起的相同超对齐问题引起的:
但那只是因为代码还有另外一个问题.
从原始循环开始:
for(i=1;i<SIZE-1;i++)
for(j=1;j<SIZE-1;j++) {
res[j][i]=0;
for(k=-1;k<2;k++)
for(l=-1;l<2;l++)
res[j][i] += img[j+l][i+k];
res[j][i] /= 9;
}
Run Code Online (Sandbox Code Playgroud)
首先注意两个内环是微不足道的.它们可以按如下方式展开:
for(i=1;i<SIZE-1;i++) {
for(j=1;j<SIZE-1;j++) {
res[j][i]=0;
res[j][i] += img[j-1][i-1];
res[j][i] += img[j ][i-1];
res[j][i] += img[j+1][i-1];
res[j][i] += img[j-1][i ];
res[j][i] += img[j ][i ];
res[j][i] += img[j+1][i ];
res[j][i] += img[j-1][i+1];
res[j][i] += img[j ][i+1];
res[j][i] += img[j+1][i+1];
res[j][i] /= 9;
}
}
Run Code Online (Sandbox Code Playgroud)
这样就留下了我们感兴趣的两个外环.
现在我们可以看到问题在这个问题中是一样的:为什么在迭代2D数组时,循环的顺序会影响性能?
您是按列而不是按行迭代矩阵.
要解决此问题,您应该交换两个循环.
for(j=1;j<SIZE-1;j++) {
for(i=1;i<SIZE-1;i++) {
res[j][i]=0;
res[j][i] += img[j-1][i-1];
res[j][i] += img[j ][i-1];
res[j][i] += img[j+1][i-1];
res[j][i] += img[j-1][i ];
res[j][i] += img[j ][i ];
res[j][i] += img[j+1][i ];
res[j][i] += img[j-1][i+1];
res[j][i] += img[j ][i+1];
res[j][i] += img[j+1][i+1];
res[j][i] /= 9;
}
}
Run Code Online (Sandbox Code Playgroud)
这完全消除了所有非顺序访问,因此您不再在大功率二次上获得随机减速.
酷睿i7 920 @ 3.5 GHz
原始代码:
8191: 1.499 seconds
8192: 2.122 seconds
8193: 1.582 seconds
Run Code Online (Sandbox Code Playgroud)
互换的外循环:
8191: 0.376 seconds
8192: 0.357 seconds
8193: 0.351 seconds
Run Code Online (Sandbox Code Playgroud)
bok*_*kan 57
以下测试是使用Visual C++编译器完成的,因为默认的Qt Creator安装使用它(我猜没有优化标志).使用GCC时,Mystical的版本与我的"优化"代码之间没有太大区别.所以结论是编译器优化比人类更好地处理微优化(我最后).我留下余下的答案供参考.
以这种方式处理图像效率不高.最好使用单维数组.处理所有像素是在一个循环中完成的.可以使用以下方法随机访问点:
pointer + (x + y*width)*(sizeOfOnePixel)
Run Code Online (Sandbox Code Playgroud)
在这种特殊情况下,最好水平计算和缓存三个像素组的总和,因为它们每次使用三次.
我做了一些测试,我认为值得分享.每个结果平均有五个测试.
用户1615209的原始代码:
8193: 4392 ms
8192: 9570 ms
Run Code Online (Sandbox Code Playgroud)
神秘的版本:
8193: 2393 ms
8192: 2190 ms
Run Code Online (Sandbox Code Playgroud)
使用1D阵列的两次传递:第一次传递用于水平和,第二次用于垂直和和平均值.两个传递寻址有三个指针,只有这样的增量:
imgPointer1 = &avg1[0][0];
imgPointer2 = &avg1[0][SIZE];
imgPointer3 = &avg1[0][SIZE+SIZE];
for(i=SIZE;i<totalSize-SIZE;i++){
resPointer[i]=(*(imgPointer1++)+*(imgPointer2++)+*(imgPointer3++))/9;
}
8193: 938 ms
8192: 974 ms
Run Code Online (Sandbox Code Playgroud)
使用一维数组进行两次传递并进行如下寻址:
for(i=SIZE;i<totalSize-SIZE;i++){
resPointer[i]=(hsumPointer[i-SIZE]+hsumPointer[i]+hsumPointer[i+SIZE])/9;
}
8193: 932 ms
8192: 925 ms
Run Code Online (Sandbox Code Playgroud)
一次缓存水平求和只是前面一行,所以它们保留在缓存中:
// Horizontal sums for the first two lines
for(i=1;i<SIZE*2;i++){
hsumPointer[i]=imgPointer[i-1]+imgPointer[i]+imgPointer[i+1];
}
// Rest of the computation
for(;i<totalSize;i++){
// Compute horizontal sum for next line
hsumPointer[i]=imgPointer[i-1]+imgPointer[i]+imgPointer[i+1];
// Final result
resPointer[i-SIZE]=(hsumPointer[i-SIZE-SIZE]+hsumPointer[i-SIZE]+hsumPointer[i])/9;
}
8193: 599 ms
8192: 652 ms
Run Code Online (Sandbox Code Playgroud)
结论:
我相信它可以做得更好.
注意 请注意,我写了这个答案来解决一般性能问题,而不是Mystical的优秀答案中解释的缓存问题.一开始它只是伪代码.我被要求在评论中做测试......这是一个完全重构的测试版本.
| 归档时间: |
|
| 查看次数: |
85396 次 |
| 最近记录: |