帮助装配/ SSE乘法

时间:2010-06-02 20:52:39

标签: c++ inline-assembly intrinsics

我一直在努力弄清楚如何在非常关键的几行中对代码进行一些改进:

float x = a*b;
float y = c*d;
float z = e*f;
float w = g*h;

所有a,b,c ......都是花车。

我决定考虑使用SSE,但似乎无法找到任何改进,事实上它的速度是原来的两倍。我的SSE代码是:

Vector4 abcd, efgh, result;
abcd = [float a, float b, float c, float d];
efgh = [float e, float f, float g, float h];
_asm {
movups xmm1, abcd
movups xmm2, efgh
mulps xmm1, xmm2
movups result, xmm1
}

我也尝试过使用标准的内联汇编,但似乎没有像我可以用SSE那样用四个浮点打包寄存器。

任何评论或帮助将不胜感激,我主要需要理解为什么使用SSE的计算比串行C ++代码慢?

我正在使用带有HT的Pentium 4在Windows XP上的Visual Studio 2005中进行编译,如果它提供任何其他信息来协助。

提前致谢!

5 个答案:

答案 0 :(得分:3)

您使用的是未对齐的指令,这些指令非常慢。 您可能希望尝试正确对齐数据,16字节边界和使用movaps。 你更好的选择是使用内在函数,而不是汇编,因为编译器可以随意订购指令。

答案 1 :(得分:3)

正如您所知,只是用SSE替换几条指令是行不通的,因为您需要在内存中随机移动数据才能正确加载SSE寄存器,并在内存中移动数据(由于内存非常慢(硬盘放在一边,内存总是这些天的瓶颈),因此构造数组的位将会破坏你的性能。

此外,如果不使用写入RAM然后读取,则无法在SSE和FPU / ALU之间移动数据。现代IA32芯片很好地应对了这种特殊模式(写入然后读取),但仍然会使一些缓存失效,这将会产生影响。

为了充分利用SSE,您需要查看整个算法和算法使用的数据。 a,b,c和d以及e,f,g和h的值需要永久地存在于那些阵列中,以便在加载SSE寄存器之前在存储器中没有移位数据。它并不简单,可能需要对代码和数据进行大量的重新处理(您可能需要以不同的方式将数据存储在磁盘上)。

也许值得指出的是SSE只有32位(如果你使用双打就是64位)而FPU是80位(无论浮点数还是双倍),因此与使用FPU相比,使用SSE时会得到略微不同的结果。只有你知道这是否会成为一个问题。

答案 2 :(得分:1)

您可以在较新的VS版本和2005年的程序选项中启用SSE和SSE2。使用快速版本进行编译?

此外,SSE中的代码可能较慢,因为编译串行C ++时,编译器很聪明,并且非常快速地完成它 - 例如,在适当的时候自动将它们放入正确的寄存器中。如果操作是串行进行的,则编译器可以减少缓存和分页的影响。然而,内联汇编程序可以最好地进行优化,并且应尽可能避免使用。

此外,您必须为SSE ​​/ 2执行大量工作才能带来显着的好处。

答案 3 :(得分:1)

这是一个旧线程,但我注意到你的例子中有一个错误。如果你想执行此操作:

float x = a*b;
float y = c*d;
float z = e*f;
float w = g*h;

然后代码应该是这样的:

Vector4 aceg, bdfh, result;  // xyzw
abcd = [float a, float c, float e, float g];
efgh = [float b, float d, float f, float h];
_asm {
movups xmm1, abcd
movups xmm2, efgh
mulps xmm1, xmm2
movups result, xmm1
}

为了获得更高的速度,我建议你不要使用单独的寄存器来获得“结果”。

首先,并非所有算法都有利于在SSE中重写。数据驱动算法(如由查找表驱动的算法)不能很好地转换为SSE,因为大量时间会丢失打包并将数据解压缩到SSE中以进行操作。

希望这仍有帮助。

答案 4 :(得分:0)

首先,当你有128bit(16byte)对齐的东西时,你应该使用MOVAPS,因为它可以更快。 编译器通常应该为您提供16字节对齐,即使在32位系统上也是如此。

您的C / C ++行与您的sse代码不同。

一个xmm寄存器中的四个浮点数乘以另一个寄存器中的四个浮点数。 给你:

float x = a*e;
float y = b*f;
float z = c*g;
float w = d*h;

在sse1中,你必须使用SHUFPS来重新排序两个寄存器中的浮点数。

同样,为了处理比cpu缓存更大的数据,您可以使用非临时存储(MOVNTPS)来减少缓存污染。 请注意,在其他情况下,非临时存储会慢得多。