C++ 为什么我的程序在循环8192个元素时速度慢?

C++ 为什么我的程序在循环8192个元素时速度慢?,c++,performance,memory-management,gcc,C++,Performance,Memory Management,Gcc,以下是有关程序的摘录。矩阵img[][]具有大小×大小,并在以下位置初始化: 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;

以下是有关程序的摘录。矩阵
img[][]
具有大小×大小,并在以下位置初始化:

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;
}
编译器是GCC。 据我所知,这是因为内存管理,但我对这个主题了解不多,这就是我在这里提问的原因

另外,如何解决这个问题也很好,但如果有人能解释这些执行时间,我已经很高兴了


我已经知道malloc/free,但问题不在于使用了多少内存,而在于执行时间,所以我不知道这会有什么帮助。

这种差异是由以下相关问题中的相同超级对齐问题造成的:

但这只是因为代码还有一个问题

从原始循环开始:

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;
}
互换外环:

8191: 0.376 seconds
8192: 0.357 seconds
8193: 0.351 seconds

下面的测试是用VisualC++编译器完成的,因为它是默认的QT创建者安装使用的(我猜没有优化标志)。当使用GCC时,Mystical的版本和我的“优化”代码之间并没有太大区别。因此,结论是编译器优化比人类(最后是我)更好地解决了微观优化问题。我把剩下的答案留作参考


以这种方式处理图像是不高效的。最好使用一维数组。处理所有像素是在一个循环中完成的。可以使用以下方法随机访问点:

pointer + (x + y*width)*(sizeOfOnePixel)
在这种特殊情况下,最好水平计算并缓存三个像素组的总和,因为每个像素组使用三次

我做了一些测试,我认为值得分享。每个结果是五次测试的平均值

用户的原始代码1615209:

8193: 4392 ms
8192: 9570 ms
神秘的版本:

8193: 2393 ms
8192: 2190 ms
使用一维数组进行两次传递:第一次传递用于水平和,第二次传递用于垂直和和平均。 具有三个指针的双通道寻址,且仅以如下方式递增:

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
for(i=SIZE;i<totalSize-SIZE;i++){
    resPointer[i]=(hsumPointer[i-SIZE]+hsumPointer[i]+hsumPointer[i+SIZE])/9;
}

8193: 932 ms
8192: 925 ms
imgPointer1=&avg1[0][0];
imgPointer2=&avg1[0][SIZE];
imgPointer3=&avg1[0][SIZE+SIZE];

对于(i=尺寸;i@bokan当大小是缓存关键步数的倍数时,就会发生这种情况。@神秘,这无关紧要,它暴露了相同的问题;代码可以不同,但基本上这两个问题问的时间是相同的(而且它们的标题绝对相似)你不应该使用2维数组来处理图像,如果你想要高性能的话。考虑所有的像素都是原始的,并将它们像一维数组一样处理。在两遍中做这个模糊。首先用3个像素的滑动和加上周围像素的值:幻灯片+SRC[I+1] -SRC[I-1 ];Dist[i]=slideSum;。然后垂直进行同样的操作,同时进行除法:dest[i]=(src[i-width]+src[i]+src[i+width])/9。这里实际上有两件事。这不仅仅是超级对齐(只是对答案的一个小挑剔。对于第一个代码段,如果所有For循环都有大括号就好了。)我还将注意到,展开内部循环对性能没有影响。编译器可能会自动进行。我展开它们的唯一目的是消除它们,以便更容易发现外部循环的问题。通过缓存每行的和,您可以将代码的速度提高三倍。但这并不是问题所在她的优化超出了原始问题的范围。@ClickUpvote这实际上是一个硬件(缓存)问题。它与语言无关。如果您在编译或JIT为本机代码的任何其他语言中尝试过它,您可能会看到相同的效果。@ClickUpvote:您似乎有点误入歧途。这是“第二个循环”这是一个你的编译器几乎肯定会做的事情,而神秘只是为了让外部循环的问题变得更加明显。这绝不是你应该自己去做的事情。这是一个很好的答案的完美例子:引用了类似的问题,step一步一步地解释你是如何处理它的,解释了问题,解释了如何解决问题,有很好的格式,甚至还有一个在你的机器上运行的代码示例。感谢你的贡献。“我认为它至少快了3倍”-你想用一些指标或引用来支持这个说法吗?@AdamRosenfield“我认为”=假设!=“是”=声明。我没有这方面的指标,我希望看到一个测试。但我的测试需要7个增量、2个子、2个加法和每个像素一个div。每个循环使用的局部变量比CPU中的寄存器少。其他循环需要7个增量、6个减量、1个div和10到20个mul,用于寻址,具体取决于编译器优化。每个指令In循环需要上一条指令的结果,这就放弃了奔腾超标量体系结构的优点。因此它必须更快。原始问题的答案都是关于内存和缓存效果。OP的代码之所以如此缓慢,是因为它的内存访问模式是按列而不是按行进行的,而按行进行的内存访问模式具有更高的效率引用的缓存局部性差。在8192处尤其糟糕,因为连续行最终在直接映射缓存或低关联性缓存中使用相同的缓存线,因此缓存未命中率更高。通过大幅度增加缓存局部性,交换循环提供了巨大的性能提升。因此,尽管您可能通过计算指令数和微优化,我们可以压缩更多的性能,通过按行对数据进行单次传递以最大化缓存位置(您也这样做了)。我相信这会带来3倍(或更多)的性能提升由于循环交换,超过了原始代码,但绝对不是
pointer + (x + y*width)*(sizeOfOnePixel)
8193: 4392 ms
8192: 9570 ms
8193: 2393 ms
8192: 2190 ms
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
for(i=SIZE;i<totalSize-SIZE;i++){
    resPointer[i]=(hsumPointer[i-SIZE]+hsumPointer[i]+hsumPointer[i+SIZE])/9;
}

8193: 932 ms
8192: 925 ms
// 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