我正在努力将一些代码转换为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]
MULT。 by 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 < EM> S-PX C]
载入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.001444ms而SSE代码运行在0.00198ms。
让我知道是否有任何我可以做的事情来进一步解释/清理这一点。我尝试使用SSE的原因是因为我运行了这个计算数百万次,这是减慢我当前代码速度的一部分。
提前感谢您的帮助! 布雷特
答案 0 :(得分:9)
进行这种矢量化的常用方法是将问题“转向”。您可以同时计算四个ox
值和四个oy
值,而不是计算ox
和oy
的单个值。这最大限度地减少了浪费的计算和混乱。
为了做到这一点,您将多个x
,y
,p2x
和p2y
值捆绑到连续的数组中(即您可能有一个包含四个值的数组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上也是如此。另一方面,如果可能的话,一次做四个,会更快。