我试图编写用于访问图像像素的优化代码,并且需要在不进入汇编级别的情况下超快地生成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”部分。循环缓慢。
答案 0 :(得分:6)
如果您拥有所有优化级别,那么其他代码没有太多理由可以提供比您给出的更好的性能。
为什么你怀疑循环本身是一个瓶颈?如果不知道你在做什么,可以说不多。对您的代码进行基准测试,如果您有疑问,请查看它产生的汇编程序。
编辑:在显示循环的内部部分之后。
在循环之外尽可能多地放置索引计算表达式的可能性。由于它与循环变量混合在一起,因此可能无法对其进行优化。 (或者只是重新排序索引的计算,以便编译器可以看到它并且可以尽可能地预先计算。)
最有可能性能困难来自您的载体的访问。如果你设法更好地计算你的指数,这也可能会改善,因为编译器/系统实际上会看到你以常规模式访问你的向量。
如果这没有帮助,请重新组织循环,使得向量的负载是增量的而不是存储。负载总是必须等到数据执行操作,存储不太明智。
答案 1 :(得分:2)
您可以展开最里面的循环。您将失去可读性,但CPU的缓存和预取队列将会做得更好。虽然这总是如此,但我不知道你会获得多少速度。
您可以将indc
和indr
都声明为寄存器变量,并尝试避免重新计算(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; ){