我需要每秒进行240000次矩阵向量乘法。矩阵为5x5,并且始终相同,而向量在每次迭代时都会变化。数据类型为float
。我正在考虑使用一些SSE(或类似的)说明。
我担心算术运算的数量与所涉及的存储器运算的数量相比太少。您认为我可以得到一些明显的改善(例如> 20%)吗?
我需要英特尔编译器吗?
您能指出一些参考吗?
[如果使用的是GCC,请注意-O3选项将启用自动矢量化,在许多情况下,它将自动生成SSE或AVX指令。通常,如果只是将其编写为简单的for循环,则GCC会将其向量化。有关更多信息,请参见http://gcc.gnu.org/projects/tree-ssa/vectorization.html。
原则上,使用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中的混洗/混合来完成。一旦有了这种格式,矩阵乘积将非常有效。
不是使用标量代码执行O(5x5x4)操作,而是仅执行O(5x5)操作,即4倍加速。使用AVX时,矩阵U
将为5x8,因此无需进行O(5x5x8)运算,而仅对O(5x5)进行赋值,即加速8倍。然而,矩阵V
将采用xxxxyyyyzzzzwwwwtttt格式,因此根据应用,可能必须将其转换为xyzwtxyzwtxyzwtxyzwt格式。 对接下来的四个矢量重复此操作(对于AVX,重复8个,依此类推,直到完成。)>如果您控制矢量,例如,如果您的应用程序是动态生成矢量,则可以以
xxxxyyyyzzzzwwwwtttt
格式生成矢量,并避免数组的转置。在这种情况下,使用SSE的速度应提高4倍,使用AVX的速度应提高8倍。如果将此与线程结合使用,例如OpenMP,使用SSE的速度应接近16倍(假设有四个物理内核)。我认为这是使用SSE最好的方法。编辑:由于指令级并行性(ILP),您可以在加速方面获得另外2倍的结果,因此,使用四核(64x AVX)可以使SSE的加速达到32倍,而由于FMA3,Haswell的获得2倍。_mm_dp_ps
,一个_mm_mul_ps
,两个_mm_add_ps
,一个普通的乘法,以及一些随机播放,装入和存储(如果矩阵是固定的,如果不需要它们,您可以将其大部分保留在SSE寄存器中。]关于内存带宽:当内存带宽以一位数千兆字节/秒为单位时,我们谈论的是2.4兆字节的向量。
将内存换为周期的经典优化技术...
SGEMV
2级矩阵向量例程之后进行y = A*x
样式操作。[如果您确实想自己实现某些功能,则在某些情况下,使用(可用)SSE..SSE4
和AVX
指令集可以显着提高性能,尽管这正是一个良好的BLAS库所要做的。您还需要考虑很多有关缓存友好的数据访问模式的问题。
我不知道这是否适用于您的情况,但是您可以一次处理向量的“块”吗?因此,您可以对y = A*x
的块进行操作,而不必重复执行[y1 y2 ... yn] = A * [x1 x2 ... xn]
样式的操作。如果是这样,则意味着您可以使用优化的矩阵矩阵例程,例如SGEMM
。由于数据访问模式,这可能比重复调用SGEMV
更有效。如果是我,我会尝试走这条路...
希望这会有所帮助。
如果内存是连续的,则不必过多担心内存操作。如果您有一个链表或其他内容,那么您会遇到麻烦,但是它应该能够保持正常运行而不会造成太大问题。
5x5是一个有趣的大小,但是您可以在一条SSE指令中至少执行4次翻牌,并尝试减少算术开销。您不需要Intel编译器,但可能会更好,我听说过有关算术代码如何更好的传说。 Visual Studio具有处理SSE2的内在函数,我认为SSE4取决于您的需求。当然,您必须自己滚动。在这里抢图书馆可能是明智之举。
_mm_dp_ps
,一个_mm_mul_ps
,两个_mm_add_ps
,一个普通的乘法,以及一些随机播放,装入和存储(如果矩阵是固定的,如果不需要它们,您可以将其大部分保留在SSE寄存器中。]SGEMV
2级矩阵向量例程之后进行y = A*x
样式操作。