我想将大小为N的浮点向量与大小为NxM的矩阵相乘。
该矩阵是一个二进制矩阵(仅包含零和1),并且相对稀疏:非零值的密度在1-5%之间。
目前,我将其形成为密集向量和稀疏浮点矩阵乘法。
但是那只是一个过度的杀伤力,不是吗?
如果我将矩阵的列存储为位集,然后相乘只是将位集用于索引向量,然后对其求和,该怎么办?
我想我可以将其形成为SSE / AVX中的向量化操作,例如load +和+ sum或load + mask + sum]
如果能为我指出正确的内在函数,我将不胜感激,主要问题是处理拆包位的最佳方法是什么?
因此,结果向量的每个元素都是输入向量的掩码和?而且这些掩码来自矩阵的列,因此它们不是连续的位。
使用连续位图的掩码和与AVX512无关紧要(只需使用合并掩码加法或零掩码加载)。对于SSE / AVX2,您将使用is there an inverse instruction to the movemask instruction in intel avx2? + _mm256_and_ps
。或针对跨掩码向量进行优化的变体,例如具有32位广播负载,然后将其转移到下一步。而不是为每个字节广播另一个未对齐的双字。
但是您的掩码位not是连续的,您可以选择:
__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吞吐量相竞争,否则前端吞吐量成本就很高。