c中循环最快的是什么?

时间:2011-01-21 13:16:47

标签: c loops for-loop

我试图编写用于访问图像像素的优化代码,并且需要在不进入汇编级别的情况下超快地生成for循环。此外,沿着行完成索引以最小化缓存未命中。

这就是我所拥有的:

for (indr=0;indr<(height-1)*width;indr+=width) {
        for (indc=0;indc<width;indc++){
            I[indr+indc]= dostuff ;
        }
    }

我不能使它成为一个循环,因为“dostuff”包括访问不在同一行的元素。

有更快的方法吗?

修改 好的,因为我之前的帖子有点不清楚,我在这里添加完整的代码。它非常难以理解,但一般的想法是我用一个简单的盒子使用积分图像进行卷积。图像首先在左侧和底部用ws + 1个零填充,在右侧和顶部用ws零填充。然后将其制成整体图像Ii。以下函数获取积分图像并提取卷积,其中结果Ic与原始图像的大小相同。

void convI(float *Ic,float *Ii,int ws, int width, int height)
{
    int W=width+ws*2+1,indR;
    int H=height+ws*2+1,indC;
    int w=width, indr;
    int h=height, indc;
    int jmpA=W*(ws+1),jmpC=W*ws,jmpB=ws+1,jmpD=ws;

    for (indR=W*(ws+1),indr=0;indr<width*(height-1);indR+=W,indr+=width) {
        for (indC=ws+1,indc=0;indc<width;indC++,indc++){
            //Performs I[indA]+I[indD]-I[indB]-I[indC];
            Ic[indr+indc]=
            Ii[indR-jmpA+indC-jmpB]+
            Ii[indR+jmpC+indC+jmpD]-
            Ii[indR+jmpC+indC-jmpB]-
            Ii[indR-jmpA+indC+jmpD];
        }
    }
}

那就是“dostuff”部分。循环缓慢。

6 个答案:

答案 0 :(得分:6)

如果您拥有所有优化级别,那么其他代码没有太多理由可以提供比您给出的更好的性能。

为什么你怀疑循环本身是一个瓶颈?如果不知道你在做什么,可以说不多。对您的代码进行基准测试,如果您有疑问,请查看它产生的汇编程序。

编辑:在显示循环的内部部分之后。

在循环之外尽可能多地放置索引计算表达式的可能性。由于它与循环变量混合在一起,因此可能无法对其进行优化。 (或者只是重新排序索引的计算,以便编译器可以看到它并且可以尽可能地预先计算。)

最有可能性能困难来自您的载体的访问。如果你设法更好地计算你的指数,这也可能会改善,因为编译器/系统实际上会看到你以常规模式访问你的向量。

如果这没有帮助,请重新组织循环,使得向量的负载是增量的而不是存储。负载总是必须等到数据执行操作,存储不太明智。

答案 1 :(得分:2)

您可以展开最里面的循环。您将失去可读性,但CPU的缓存和预取队列将会做得更好。虽然这总是如此,但我不知道你会获得多少速度。 您可以将indcindr都声明为寄存器变量,并尝试避免重新计算(height-1)*width,而是将其保存在临时变量中。你知道,乘法会占用很多时钟周期......

答案 2 :(得分:1)

除非您想使用像SSE这样的矢量化指令,否则无法做到。

答案 3 :(得分:1)

你看起来很好。如果你想避免进入汇编,最好简单的循环。 GCC很聪明。如果您清楚自己希望代码执行什么操作,那么通常可以很好地优化代码。但是,如果您在生产代码中执行不常见的花哨技巧,则可能无法推断出您“真正意味着”的内容。

根据dostuff实际执行的操作,您可能会在临时缓存I[indr+indc]中获得一些胜利,因此您的代码看起来像......

char t = I[indr+indc];
// do stuff
I[indr+indc] = t;

此代码的性能不会更差(我假设您至少已启用了基本优化),但如果您的do stuff足够花哨(如果您愿意,我可以详细说明),它可能会表现得更好。

不要听其他人从循环中提取简单的数学。真的没有必要。如果你看一下在-O1生成的程序集,你会看到每次都为你完成。这是最便宜的优化之一。

答案 4 :(得分:0)

在循环之前将外循环中的height-1提升为赋值可能会获胜。但是,我怀疑这些天的普通编译器会将其作为标准优化。它也可能是有另一个指针,设置为I [indr]然后索引可能是一个小胜利。

这两个都需要一些非常仔细的基准测试才能注意到。

答案 5 :(得分:0)

// DragonLord style:
float *ic_p = I + (width * height) - 1;  // fencepost  
// Start at the end, and work backwards 
// assumes I is 0-based and wraps, is contiguous

for (indr=(height -1) * width; indr>=0; indr-=width ) {
// Sadly cannot test on indr -= width here
// as the 0 pass is needed for the loop
        for (indc=width; indc--; ){
        // Testing on postdecrement
        // allows you to use the 0 value one last time before testing it FTW
            // indr and indc are both 0-based inside the loop for you
            // e.g. indc varies from (width-1) down to 0
            // due to postdecrement before usage
            printf( "I[ %d + %d ] == %f \n", indr, indc, *ic_p );
            // always use pointers in C/C++ for speed, we are not Java
            *ic_p-- = dostuff ;
        }
    }
如果你不需要在循环中使用indr,或者如果你可以通过基于1的方式使用precmenting而不是postdecrementing indc,则可以通过从高度向下倒数0来略微提高性能indc,在这种情况下,indc应该初始化为(width +1):

   for (indc=(width+1); --indc; ){