2

我一直试图弄清楚如何在非常关键的几行代码中获得一些改进:

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++ 代码慢?

我在 Windows XP 上的 Visual Studio 2005 中编译,使用带有 HT 的 Pentium 4,如果这提供了任何额外的信息。

提前致谢!

4

5 回答 5

3

您正在使用未对齐的指令,这些指令非常慢。您可能想尝试正确对齐数据、16 字节边界并使用 movap。你更好的选择是使用内在函数,而不是汇编,因为编译器可以自由地订购指令,因为它似乎是必要的。

于 2010-06-02T21:15:46.167 回答
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 时得到的结果会略有不同。只有您知道这是否会成为问题。

于 2010-06-02T21:33:36.043 回答
1

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

此外,您在 SSE 中的代码可能更慢,因为当您编译串行 C++ 时,编译器很智能,并且在使其速度非常快方面做得非常好——例如,在正确的时间自动将它们放入正确的寄存器中。例如,如果操作串行发生,编译器可以减少缓存和分页的影响。然而,内联汇编器最多只能优化得很差,应尽可能避免使用。

此外,您必须为 SSE/2 执行大量工作才能带来显着收益。

于 2010-06-02T21:08:41.630 回答
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 运行。

希望这仍然有帮助。

于 2012-04-18T18:46:32.743 回答
0

首先,当你有 128 位(16 字节)对齐的东西时,你应该使用 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) 来减少缓存污染。请注意,在其他情况下,非临时存储要慢得多。

于 2013-05-18T02:47:55.363 回答