SIMD 优化小矩阵乘法 (16 x 16) x (16 x 1)

SIMD optimize small matrix multiply (16 x 16) x (16 x 1)

在 AVX-512 中执行 (16 x 16) 浮点矩阵 M 与 (16 x 1) 向量 V 相乘的最佳方法是什么?我能想到的方法是使用 _mm512_fmadd_ps 对矩阵的每一行与 V 进行元素相乘,然后使用 _mm512_reduce_add_ps 对每个结果向量进行水平求和。总共 16 fmadd 次调用和 16 reduce_add 次调用。

我的理解是水平添加步骤相当慢,但是。我们是否期望这种方法比简单的非矢量化 C++ 实现快得多?有没有比这种方法更好的利用 SIMD 的方法?

理想情况下,您可以布置输入数据,这样您就可以使用广播内存源(来自向量的每个元素)进行 16x FMA,即矩阵已经转置。然后结果将是 16x16 和 16x1 输入之间的行 x 列点积的向量。

(实际上是 1 个 vmulps 和 15x FMA。或者可能更好,通过从 2 或 4 个普通乘法开始公开一些指令级并行性,并且只在最后组合这些 FMA 依赖链。这将需要额外的 vaddps 对于每个额外的向量累加器,但会缩短关键路径延迟,并通过没有 16 * 4 周期延迟依赖链来尝试隐藏来减轻乱序执行的负担。)

仅使用 AVX,而非 AVX512,广播负载不能用作内存源操作数对于 FMA 指令,但仍然仅花费 1 条单 uop 指令(vbroadcastss ymm, [mem]).但实际上,如果两个操作数都来自内存,那是无关紧要的;编译器可以选择单独进行广播加载并使用完整的行向量作为内存源操作数。


否则你不想分开 _mm512_reduce_add_ps每个向量;而是使用 2x _mm512_hadd_ps(每次有 2 个不同的向量)转置和添加向量对,然后进行一些手动洗牌和添加,直到将每个元素的 16x __m512 减少到一个 __m512是原始 16 个向量之一的水平和。

在第二种情况下,我想你只想在你的向量和矩阵的一行之间直接 _mm512_mul_ps;没有什么可补充的。

_mm512_reduce_add_ps 不是一条机器指令;它通常会编译为 4x shuffle + 4x vaddps.
相比之下,当我们洗牌时,2 次洗牌来喂养每个加法以减少,应该将 16 个向量组合为 8 + 4 + 2 + 1 = 15 次总加法(和 30 次总洗牌)中的 1 个,而不是 16 * (4,4)