向量矩阵乘法,浮点向量,二进制矩阵

问题描述 投票:0回答:1

我想将大小为N的浮点向量与大小为NxM的矩阵相乘。

该矩阵是一个二进制矩阵(仅包含零和1),并且相对稀疏:非零值的密度在1-5%之间。

目前,我将其形成为密集向量和稀疏浮点矩阵乘法。

但是那只是一个过度的杀伤力,不是吗?

如果我将矩阵的列存储为位集,然后相乘只是将位集用于索引向量,然后对其求和,该怎么办?

我想我可以将其形成为SSE ​​/ AVX中的向量化操作,例如load +和+ sum或load + mask + sum]

如果能为我指出正确的内在函数,我将不胜感激,主要问题是处理拆包位的最佳方法是什么?

c++ matrix-multiplication sse simd avx
1个回答
0
投票

因此,结果向量的每个元素都是输入向量的掩码和?而且这些掩码来自矩阵的列,因此它们不是连续的位。

使用连续位图的掩码和与AVX512无关紧要(只需使用合并掩码加法或零掩码加载)。对于SSE / AVX2,您将使用is there an inverse instruction to the movemask instruction in intel avx2? + _mm256_and_ps。或针对跨掩码向量进行优化的变体,例如具有32位广播负载,然后将其转移到下一步。而不是为每个字节广播另一个未对齐的双字。

但是您的掩码位not是连续的,您可以选择:

  • 分别处理每个输出矢量元素,并在末尾添加一个水平和。需要收集位并制作矢量掩码。除了M = 32的情况外,大概有点困难,在这种情况下,位跨度已经使它们与连续的32位浮点对齐。
  • 累加4或8个输出元素的向量
  • ,使用4或8个掩码位的连续组。因此,您可以对外部循环而非内部循环进行矢量化处理。实际上,您应该展开多个矢量和来隐藏FP添加延迟。

    __m256 v = _mm256_set1_ps(invec[i])之类的广播负载基本上是免费的(vbroadcastss是纯负载uop,没有ALU随机播放uop)。您甚至不需要在循环末尾进行任何其他的浮点改组,只需纯垂直SIMD:您只需将_mm256_storeu_ps插入输出向量即可。

而且您使用的是连续的掩码位组,因此通常使用反向移动掩码Q&A很有用。

这对于内存带宽也非常有用:您只循环输入向量M / 8次,或更少,展开更多。

每个和向量的掩码加法使用不同的掩码位,但广播的输入相同[C0 ]。由于矩阵元素比向量元素小32倍,所以这是相当不错的。

如果您的float矩阵恰好具有NxM,那么它与M = 32的大小完全匹配,并且float将获得一个向量,该向量在每个元素的低位具有_mm256_loadu_si256的掩码位。高位outvec[0]的屏蔽位。您可以使用outvec[31]将它们应用于求和输入,然后向左移1以将下一位向上移动到最高位置。 (_mm256_blendv_ps的替代方法是vblendvps除以​​31 + psrad:算术右移以将最高位广播到所有位置)。

但是即使对于这种特殊情况,这也可能不会比其他方法更好。您可以展开不同向量中的多个输出元素,以便可以重复使用float向量几次。


使用AVX512F,您仅可以将矩阵行用作andps__mmask16值。如果a masked add like _mm512_mask_add_ps_mm512_mask_add_ps的数组,则为sum = _mm512_mask_add_ps(sum, matrix[col*rowstride + row], sum, invec[i]);

或使用AVX512BW,将matrix 64位掩码放入uint16_t寄存器中,然后将kmovq向下掩码,以与展开4个向量累加器相匹配。不幸的是,k在Skylake-X上是2块:负载+端口5,而不仅仅是可以写入掩码regs的负载uop。因此,用kshift进行3倍解压缩的负载是对4xkmov k, [mem]/ kshift等的纯胜。如果没有端口5 uop,则无法在kmovw k1, [mem]寄存器的底部获得每个16位掩码数据。一。因此,它在具有2个FMA单元的SKX内核上可与512位FMA / add / mul吞吐量相竞争,否则前端吞吐量成本就很高。

© www.soinside.com 2019 - 2024. All rights reserved.