6

我正在努力将一些代码转换为 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
    }
}

由于它很难阅读代码,我将解释我做了什么:

加载向量4,xmm0 _____ p = [px,py,px,py]
mult。通过vector4,xmm1 _ cs = [c,c,s,s]
__________________________mult----------------------------
结果,_____________ xmm0 = [px c, py c, px s, py s]

重用结果,xmm0 = [px c, py c, px s, py s]
洗牌结果, xmm2 = [py s, px s, py c, px c]
_____________________减法---------- ---------------
结果,xmm0 = [px c-py s,py c-px s,px s-py c,py s-px c]

重用结果,xmm0 = [px c-py s, py c-px s, px s-py c, py s-px c]
load m vector4, scale = [m, m, m, m]
__________________________mult---- ------------------------
结果,xmm0 = [(px c-py s) m, (py c-px*s) m, (px s-py*c) m, (py s-px*c) m]


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

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

对于阅读本文的困难,我深表歉意,但我希望有人能够为我提供一些指导,因为标准 c++ 代码运行时间为 0.001444 毫秒,而 SSE 代码运行时间为 0.00198 毫秒。

让我知道我是否可以做些什么来进一步解释/清理一下。我尝试使用 SSE 的原因是因为我运行了这个计算数百万次,它是减慢我当前代码的一部分。

提前感谢您的帮助!布雷特

4

1 回答 1

9

进行这种矢量化的常用方法是将问题“放在一边”。ox您无需计算and的单个值,而是同时oy计算四个ox值和四个oy值。这最大限度地减少了浪费的计算和洗牌。

为此,您将多个xy和值捆绑到连续数组中(即,您可能有一个包含四个 值的p2x数组、一个包含四个 值的数组,等等)。然后你可以这样做:p2yxy

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吗?这会给你一个标准的旋转矩阵)

编辑

您对您正在计时的硬件的评论几乎清除了一切:“Pentium 4 HT,2.79GHz”。那是一个非常古老的微架构,未对齐的移动和洗牌非常缓慢;您在管道中没有足够的工作来隐藏算术运算的延迟,并且重新排序引擎并不像在较新的微架构上那样聪明。

我希望您的向量代码被证明比 i7 上的标量代码更快,并且可能在 Core2 上也是如此。另一方面,如果可以的话,一次做四个会更快。

于 2010-05-27T17:57:27.070 回答