通过SSE加速矩阵乘法(C ++)

时间:2011-07-07 22:04:27

标签: c++ sse matrix-multiplication

我需要每秒运行240000次矩阵向量乘法。矩阵是5x5并且始终相同,而向量在每次迭代时都会发生变化。数据类型是float。我正在考虑使用一些SSE(或类似的)指令。

1)我担心与所涉及的内存操作数相比,算术运算的数量太少。你认为我可以获得一些有形的(例如> 20%)改善吗?

2)我需要英特尔编译器吗?

3)你能指出一些参考文献吗?

谢谢大家!

8 个答案:

答案 0 :(得分:9)

向量,矩阵,...的Eigen C ++模板库都有

  • 针对小型固定大小矩阵(以及动态大小的矩阵)的优化代码

  • 使用SSE优化的优化代码

所以你应该试一试。

答案 1 :(得分:4)

如果您正在使用GCC,请注意-O3选项将启用自动矢量化,在许多情况下会自动生成SSE或AVX指令。一般来说,如果你只是把它写成一个简单的for循环,GCC会对它进行矢量化。有关详细信息,请参阅http://gcc.gnu.org/projects/tree-ssa/vectorization.html

答案 2 :(得分:4)

原则上,SSE的加速比可为4倍(AVX为8倍)。让我解释一下。

我们称之为固定的5x5矩阵 M 。将5D向量的分量定义为(x,y,z,w,t)。现在从前四个向量形成一个5x4矩阵 U

U =
xxxx
yyyy
zzzz
wwww
tttt

接下来,做矩阵产品 MU = V 。矩阵 V 包含 M 和前四个向量的乘积。唯一的问题是,对于SSE我们需要在 U 行中读取,但在内存 U 存储为 xyzwtxyzwtxyzwtxyzwt ,所以我们必须转置它到 xxxxyyyyzzzzwwwwtttt 。这可以通过SSE中的shuffles / blend进行。一旦我们有这种格式,矩阵产品就非常有效。

不是采用标量码进行O(5x5x4)运算,而是仅需要O(5x5)运算,即4倍加速。使用AVX时,矩阵 U 将为5x8,因此不需要进行O(5x5x8)操作,而只需对O(5x5)征税,即加速8倍。

然而,矩阵 V 将采用 xxxxyyyyzzzzwwwwtttt 格式,因此根据应用程序,它可能必须转换为 xyzwtxyzwtxyzwtxyzwt 格式。

对接下来的四个向量(AVX为8)重复此操作,依此类推,直至完成。

如果你可以控制向量,例如你的应用程序动态生成向量,那么你可以用 xxxxyyyyzzzzwwwwtttt 格式生成它们,并避免数组的转置。在这种情况下,您应该使用SSE加速4倍,使用AVX加速8倍。如果将此与线程结合使用,例如OpenMP,你的加速应该接近16倍(假设有4个物理内核)和SSE。我认为这是你能用SSE做的最好的事情。

编辑:由于指令级并行性(ILP),您可以在加速时获得另一个因子2,因此SSE的加速可以是四个核心(64x AVX)的32倍,并且由于FMA3,再次使用Haswell的另一个因子为2。

答案 3 :(得分:3)

我建议使用英特尔IPP并提取自己对技术的依赖

答案 4 :(得分:2)

这应该很容易,特别是当你在Core 2或更高版本时:你需要5 * _mm_dp_ps,一个_mm_mul_ps,两个_mm_add_ps,一个普通的乘法,加上一些shuffle,加载和存储(如果矩阵是固定的,如果您不需要其他任何东西,您可以将其中的大部分保留在sse寄存器中。

至于内存带宽:当内存带宽为每秒一位数千兆字节时,我们讨论的是2,​​4兆字节的向量。

答案 5 :(得分:1)

对载体有何了解?由于矩阵是固定的,并且如果向量可以采用有限数量的值,那么我建议您预先计算计算并使用表查找来访问它们。

为循环交换内存的经典优化技术......

答案 6 :(得分:0)

我建议您查看优化的BLAS库,例如Intel MKL或AMD ACML。根据您的描述,我假设您将在SGEMV 2级矩阵向量例程之后进行y = A*x样式操作。

如果你真的想自己实现一些东西,使用(可用的)SSE..SSE4AVX指令集可以在某些情况下提供显着的性能改进,尽管这正是一个好的BLAS库将是这样做。您还需要考虑很多有关缓存友好的数据访问模式。

我不知道这是否适用于你的情况,但是你可以一次操作矢量的“块”吗?因此,您可以在y = A*x块上操作,而不是重复执行[y1 y2 ... yn] = A * [x1 x2 ... xn]样式操作。如果是这样,这意味着您可以使用优化的矩阵矩阵例程,例如SGEMM。由于数据访问模式,这可能比重复调用SGEMV更有效。如果是我,我会试着走这条路......

希望这有帮助。

答案 7 :(得分:0)

如果您事先知道向量(例如,一次完成所有240k),那么通过并行循环比通过SSE获得更好的加速。如果您已经采取了这一步骤,或者您不能立即了解它们,那么SSE可能是一个很大的好处。

如果内存是连续的,那么不要过多担心内存操作。如果你有链接列表或其他东西,那么你遇到了麻烦,但它应该能够跟上而没有太多问题。

5x5是一个有趣的大小,但你可以在一个SSE指令中至少执行4次触发,并尝试减少算术开销。你不需要英特尔编译器,但它可能会更好,我听说有关算术代码如何更好的传说。 Visual Studio具有处理SSE2的内在函数,我认为SSE4取决于您的需求。当然,你必须自己滚动它。抓住图书馆可能是明智之举。