【问题标题】:Need some constructive criticism on my SSE/Assembly attempt需要对我的 SSE/Assembly 尝试进行一些建设性的批评
【发布时间】:2010-05-27 17:40:14
【问题描述】:

我正在努力将一些代码转换为 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 ]
多。通过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, scale = [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 = [(px
c-py*s)m, (pyc-px*s)m, (pxs-py*c )m, (pys-px*c)m]
_____________________减法--------------------------
结果,xmm3 = [p2x-(px
c-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.001444 毫秒,而 SSE 代码运行时间为 0.00198 毫秒。

如果我能做些什么来进一步解释/清理一下,请告诉我。我尝试使用 SSE 的原因是因为我运行了这个计算数百万次,这是导致我当前代码变慢的部分原因。

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

【问题讨论】:

  • 他们说今天优化编译器通常可以比普通的汇编程序员更好地优化代码。你看过编译器生成的汇编代码吗?
  • 我没有,但我会尝试进一步调查,谢谢!
  • 你在什么硬件上计时?
  • 我使用的是奔腾 4 HT,2.79GHz,运行 XP
  • 我正在阅读这篇文章 (linux-kongress.org/2009/slides/…),它说带有 -O3 优化的 gcc 4.4 很可能会为您矢量化代码。

标签: assembly x86 sse


【解决方案1】:

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

为了做到这一点,您将几个 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?这会给你一个标准的旋转矩阵)

编辑

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

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

【讨论】:

  • 我同意你的说法,但我不能一次计算多个 ox 和 oy,我必须做一个计算,然后等待我的程序做更多的工作,直到我回到这个计算。因此,我不确定是否可以像我们都希望看到的那样利用所有内存(同时计算四个浮点数)。您能想到我可以做些什么来帮助优化针对单个计算而不是一组计算的计算方式吗?谢谢!!布雷特
猜你喜欢
  • 2015-03-10
  • 1970-01-01
  • 2014-03-27
  • 2019-04-03
  • 1970-01-01
  • 1970-01-01
  • 2012-07-08
  • 2014-10-02
  • 1970-01-01
相关资源
最近更新 更多