我正在努力将一些代码转换为SSE,虽然我得到了正确的输出,但事实证明它比标准的c++代码要慢。

我需要为此做的代码是:

float ox = p2x - (px * c - py * s)*m;
float oy = p2y - (px * s - py * c)*m;

我为SSE代码准备的是:
void assemblycalc(vector4 &p, vector4 &sc, float &m, vector4 &xy)
{
    vector4 r;
    __m128 scale = _mm_set1_ps(m);

__asm
{
    mov     eax,    p       //Load into CPU reg
    mov     ebx,    sc
    movups  xmm0,   [eax]   //move vectors to SSE regs
    movups  xmm1,   [ebx]

    mulps   xmm0,   xmm1    //Multiply the Elements

    movaps  xmm2,   xmm0    //make a copy of the array
    shufps  xmm2,   xmm0,  0x1B //shuffle the array

    subps   xmm0,   xmm2    //subtract the elements

    mulps   xmm0,   scale   //multiply the vector by the scale

    mov     ecx,    xy      //load the variable into cpu reg
    movups  xmm3,   [ecx]   //move the vector to the SSE regs

    subps   xmm3,   xmm0    //subtract xmm3 - xmm0

    movups  [r],    xmm3    //Save the retun vector, and use elements 0 and 3
    }
}

由于很难阅读代码,因此我将解释我所做的事情:

已加载的vector4,xmm0 _____ p = [px,py,px,py]
多通过vector4,xmm1 _ cs = [c,c,s,s]
__________________________杂物----------------------------
结果,_____________ xmm0 = [pxc,pyc,pxs,pys]

重用结果,xmm0 = [pxc,pyc,pxs,pys]
随机播放结果,xmm2 = [pys,pxs,pyc,pxc]
_____________________减去 - - - - - - - - - - - - - -
结果,xmm0 = [pxc-pys,pyc-pxs,pxs-pyc,pys-pxc]

重用结果,xmm0 = [pxc-pys,pyc-pxs,pxs-pyc,pys-pxc]
加载m vector4,比例= [m,m,m,m]
__________________________杂物----------------------------
结果,xmm0 = [(pxc-pys)m,(pyc-px * s)m,(pxs-py * c)m,(pys-px * c)m]

加载xy vector4,xmm3 = [p2x,p2x,p2y,p2y]
重用xmm0 = [(pxc-py * s)m,(pyc-px * s)m,(pxs-py * c)m,(pys-px * c)m]
_____________________减去 - - - - - - - - - - - - - -
结果,xmm3 = [p2x-(pxc-py * s)m,p2x-(pyc-px * s)m,p2y-(pxs-py * c)m,p2y-(pys-px * c)* m]

那么ox = xmm3 [0]和oy = xmm3 [3],所以我基本上不使用xmm3 [1]或xmm3 [4]

非常抱歉阅读本文,但我希望有人能够为我提供一些指导,因为标准c++代码的运行时间为0.001444ms,SSE代码的运行时间为0.00198ms。

让我知道是否有什么我可以做的进一步解释/整理。我尝试使用SSE的原因是因为我运行了数百万次此计算,并且这是使当前代码变慢的一部分。

在此先感谢您的帮助!
布雷特

最佳答案

进行这种矢量化处理的通常方法是将问题“转为反面”。您无需同时计算oxoy的单个值,而可以同时计算四个ox值和四个oy值。这样可以最大程度地减少浪费的计算和混洗。

为此,您需要将几个xyp2xp2y值捆绑成连续的数组(即,您可能具有四个x值的数组,一个四个y值的数组,等等)。然后,您可以执行以下操作:

movups  %xmm0,  [x]
movups  %xmm1,  [y]
movaps  %xmm2,  %xmm0
mulps   %xmm0,  [c]    // cx
movaps  %xmm3,  %xmm1
mulps   %xmm1,  [s]    // sy
mulps   %xmm2,  [s]    // sx
mulps   %xmm3,  [c]    // cy
subps   %xmm0,  %xmm1  // cx - sy
subps   %xmm2,  %xmm3  // sx - cy
mulps   %xmm0,  scale  // (cx - sy)*m
mulps   %xmm2,  scale  // (sx - cy)*m
movaps  %xmm1,  [p2x]
movaps  %xmm3,  [p2y]
subps   %xmm1,  %xmm0  // p2x - (cx - sy)*m
subps   %xmm3,  %xmm2  // p2y - (sx - cy)*m
movups  [ox],   %xmm1
movups  [oy],   %xmm3

使用这种方法,我们用18条指令同时计算4个结果,而使用您的方法则只用13条指令计算单个结果。我们也没有浪费任何结果。

仍然可以在此基础上进行改进。由于必须使用该方法重新安排数据结构,因此应该对齐阵列,并使用对齐的加载和存储方式,而不要使用未对齐的方式。您应该将c和s加载到寄存器中,并使用它们处理x和y的多个向量,而不是为每个向量重新加载它们。为了获得最佳性能,应交织两个或多个值得计算的向量,以确保处理器有足够的工作来防止流水线停顿。

(附带说明:应该是cx + sy而不是cx - sy吗?这将为您提供标准的旋转矩阵)

编辑

您对正在使用哪种硬件进行计时的评论几乎可以清除所有内容:“奔腾4 HT,2.79GHz”。那是一个非常古老的微体系结构,未对齐的移动和重新排列很慢。您没有足够的工作量来隐藏算术运算的延迟,并且重新排序引擎并不像在新的微体系结构上那样聪明。

我希望您的向量代码将被证明比i7和Core2上的标量代码更快。另一方面,如果可以的话,一次执行四次仍会更快。

关于assembly - 在我的SSE/组装尝试中需要一些 build 性的批评,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/2923458/

10-10 21:02