C SIMD值得吗?有更好的选择吗?

C SIMD值得吗?有更好的选择吗?,c,optimization,simd,C,Optimization,Simd,我有一些代码运行得相当好,但我想让它运行得更好。我的主要问题是它需要一个嵌套的for循环。外部一个用于迭代(必须连续发生),内部一个用于所考虑的每个点粒子。我知道对于外部的问题我无能为力,但我想知道是否有一种方法可以优化如下内容: void collide(particle particles[], box boxes[], double boxShiftX, double boxShiftY) {/*{{{*/ int i;

我有一些代码运行得相当好,但我想让它运行得更好。我的主要问题是它需要一个嵌套的for循环。外部一个用于迭代(必须连续发生),内部一个用于所考虑的每个点粒子。我知道对于外部的问题我无能为力,但我想知道是否有一种方法可以优化如下内容:

    void collide(particle particles[], box boxes[], 
        double boxShiftX, double boxShiftY) {/*{{{*/
            int i;
            double nX; 
            double nY; 
            int boxnum;
            for(i=0;i<PART_COUNT;i++) {
                    boxnum = ((((int)(particles[i].sX+boxShiftX))/BOX_SIZE)%BWIDTH+
                        BWIDTH*((((int)(particles[i].sY+boxShiftY))/BOX_SIZE)%BHEIGHT)); 
                        //copied and pasted the macro which is why it's kinda odd looking

                    particles[i].vX -= boxes[boxnum].mX;
                    particles[i].vY -= boxes[boxnum].mY;
                    if(boxes[boxnum].rotDir == 1) {
                            nX = particles[i].vX*Wxx+particles[i].vY*Wxy;
                            nY = particles[i].vX*Wyx+particles[i].vY*Wyy;
                    } else { //to make it randomly pick a rot. direction
                            nX = particles[i].vX*Wxx-particles[i].vY*Wxy;
                            nY = -particles[i].vX*Wyx+particles[i].vY*Wyy;
                    }   
                    particles[i].vX = nX + boxes[boxnum].mX;
                    particles[i].vY = nY + boxes[boxnum].mY;
            }   
    }/*}}}*/
无效碰撞(粒子粒子[],长方体[],
双boxShiftX,双boxShiftY){/*{{{*/
int i;
双nX;
双纽约;
int-boxnum;

对于(i=0;i您是否有足够的分析来告诉您在该函数中花费的时间

例如,您确定在boxnum计算中花费的时间不是您的div和mod吗?有时编译器无法发现可能的移位/替换,即使是人类(或者至少知道BOX_大小和BWIDTH/bhweight的人,我不知道)可能会发现

花很多时间来模拟错误的代码是很遗憾的

另一件可能值得研究的事情是,是否可以将工作强制为可以与IPP这样的库一起工作的内容,这将对如何最好地使用处理器做出明智的决定

((int)(particles[i].sX+boxShiftX))/BOX_SIZE

如果sX是一个int(不知道),那么这是很昂贵的。在进入循环之前,将boxShiftX/Y截断为int。

我不确定SIMD会有多大好处;内部循环非常小且简单,所以我猜(通过查看)记住,我会尝试重写循环的主要部分,以避免超出所需的接触粒子阵列:

const double temp_vX = particles[i].vX - boxes[boxnum].mX;
const double temp_vY = particles[i].vY - boxes[boxnum].mY;

if(boxes[boxnum].rotDir == 1)
{
    nX = temp_vX*Wxx+temp_vY*Wxy;
    nY = temp_vX*Wyx+temp_vY*Wyy;
}
else
{
    //to make it randomly pick a rot. direction
    nX =  temp_vX*Wxx-temp_vY*Wxy;
    nY = -temp_vX*Wyx+temp_vY*Wyy;
}   
particles[i].vX = nX;
particles[i].vY = nY;
这有一个很小的潜在副作用,就是在最后不做额外的加法


另一个潜在的加速方法是在粒子阵列上使用
\uuuu restrict
,这样编译器可以更好地优化对速度的写入。此外,如果Wxx等是全局变量,它们可能每次都必须通过循环重新加载,而不是可能存储在寄存器中;使用
\uu restrict
将有助于我也是


由于您是按顺序访问粒子的,因此可以尝试在前面预取几个粒子(例如GCC上的
\uuuu builtin\u prefetch
),以减少缓存未命中率。由于您是按不可预测的顺序访问粒子,所以对框进行预取有点困难;您可以尝试以下操作

int nextBoxnum = ((((int)(particles[i+1].sX+boxShiftX) /// etc...
// prefetch boxes[nextBoxnum]

我刚刚注意到的最后一个问题是,如果box::rotDir始终为+/-1.0,则可以消除内部循环中的比较和分支,如下所示:

const double rot = boxes[boxnum].rotDir; // always +/- 1.0
nX =     particles[i].vX*Wxx + rot*particles[i].vY*Wxy;
nY = rot*particles[i].vX*Wyx +     particles[i].vY*Wyy;


当然,分析前后的常见警告也适用。但我认为所有这些都可能有帮助,并且无论您是否切换到SIMD都可以做到。

请注意,还有libSIMDx86


(在编译时,您也可以尝试:gcc-O3-msse2或类似的方法)。

您的算法内存、整数和分支指令太多,没有足够的独立触发器从SIMD中获益。管道将不断暂停


找到一种更有效的随机化方法将是最重要的。然后,尝试使用float或int,但不能同时使用这两种方法。将条件条件重新转换为算术,或至少作为选择运算。只有这样,SIMD才成为一个现实的命题

老实说,可能是div和mod,但不是;我还没有找到一个分析器来告诉我在我目前的实验中,BOX_大小是1,你有一个很好的观点:BWIDTH,BHEIGHT是2的幂。你对更细粒度的分析器有什么建议吗?我希望任何采样分析器都能提供每行的信息,当然编译器优化会使行匹配有点不精确。英特尔vTune将为您提供比单个汇编指令更细粒度的信息,因此,如果您认为这是您想要看到的,那么这可能是一种方法像这样,我倾向于在多次运行中对代码计时,然后对其进行破解,以查看花费的时间。不幸的是,sX和boxShiftX都是双倍的,其目的是有效地随机舍入(boxShiftX在[-.5,.5]范围内)我不知道,当浮点数需要被截断并取模时,我通常会去wtf。这是一个整数问题被模糊化的标志。一旦你去了那里,通过缩放将浮点数变成整数通常会得到很大的回报。像这样的代码的最终结果往往是整数,可能是s上的一个像素屏幕。整数结果应该有整数数学。抱歉,我只是不知道你真正想做什么来提供更多帮助。我有这组粒子,正在将它们分类到“盒子”中。但是由于模拟的一个怪癖,盒子的位置必须在每个时间步之间跳跃,这就是为什么会发生这种情况。感谢接受我的answ呃,这些有多少帮助?