有4位,如何为AVX寄存器产生口罩

Having 4 bits, how to produce a mask for AVX register?

本文关键字:寄存器 AVX 4位      更新时间:2023-10-16

_mm256_blendv_pd()查看位置63、127、191和255的位置。是否有一种有效的方法将uint8_t的4个较低位散射到AVX寄存器的这些位置中?

另外,是否有一种有效的方法来广播这些位,因此像_mm256_cmp_pd()的结果一样,每个位在AVX寄存器的相应64位组件中重复?

指令集为AVX2(如果需要其他功能,则Ryzen CPU)。

假设uint8_t在通用寄存器中存在;方法是:

  1. 使用PDEP将四位转换为四个字节(最高位)
  2. 将四个字节从32位GPR传输到YMM寄存器的低部分
  3. 将值置于适当的位置(位63、127、191、255)

所以我想出了两个版本 - 一个带有内存的版本,另一个没有:

与内存接近:

.data
  ; Always use the highest bytes of a QWORD as target / 128 means 'set ZERO' 
  ddqValuesDistribution:    .byte  3,128,128,128,128,128,128,128, 2,128,128,128,128,128,128,128, 1,128,128,128,128,128,128,128, 0,128,128,128,128,128,128,128
.code
  ; Input value in lower 4 bits of EAX
  mov     edx, 0b10000000100000001000000010000000
  pdep    eax, eax, edx
  vmovd   xmm0, eax
  vpshufb ymm0, ymm0, ymmword ptr [ddqValuesDistribution]

这是Haswell和Skylake的5个UOPS。


无内存变量的方法(感谢@Peter Cordes):

  mov  edx, 0b10000000100000001000000010000000
  pdep eax, eax, edx
  vmovd xmm0, eax 
  vpmovsxbq ymm0, xmm0

这是Haswell和Skylake(!)的4个UOPS,可以通过将Mask移动到EDX中,从而进一步改善。
输出不同于第一个版本(所有版本与最高位设置)。

最有效的方法是使用包含16位256位条目的查找向量,由UINT-8索引。

明显的解决方案:将这些4位用作索引中的索引。您已经知道这一点,所以让我们尝试一下。

基于变量的方法:将字节广播到每个QWOWS中,然后将其向左移动{63、62、61、60},在MSB中列出右键。未经测试,类似的东西:

_mm256_sllv_epi64(_mm256_set1_epi64x(mask), _mm256_set_epi64x(63, 62, 61, 60))

作为奖励,由于负载不取决于面具,因此可以将其提起。

这不一定是Ryzen上的一个好主意,来自内存的256位负载具有比仅仅是vpsllvq本身的吞吐量更高(就像Ryzen上的大多数256B操作一样2 µ,但在这里我们也有vmovq(如果该字节不是来自矢量寄存器)和宽vpbroadcastq(再次2 µOPS)。

取决于上下文,可能值得做与否。这取决于