给定一个 int 偏移向量,如何使用 AVX512 内在函数收集单个字节?

Posted

技术标签:

【中文标题】给定一个 int 偏移向量,如何使用 AVX512 内在函数收集单个字节?【英文标题】:How can I gather single bytes with AVX512 intrinsics, given a vector of int offsets? 【发布时间】:2020-05-09 18:49:07 【问题描述】:

我有一个基地址 (uint8_t*) 和一个包含 16 个偏移量的向量 (__m512i)。 我最终需要一个 __m128i,其中包含从 16 个不同内存位置收集的 16 个字节。

至于现在我明白了,没有这种原语,我能用的只有

uint8_t base;
__m512i offsets;
__m512i values = _mm512_i32gather_epi32(base, offsets, 1);

这给了我一个 __m512i 我有的地方

Vjjj Vjjj Vjjj Vjjj Vjjj Vjjj Vjjj Vjjj Vjjj Vjjj Vjjj Vjjj Vjjj Vjjj Vjjj Vjjj

(j 是垃圾,V 是我感兴趣的值)

现在我需要重新打包数据,以便最终得到一个只有我感兴趣的数据的向量,但我越来越困惑,我什至不知道我是否遵循正确的接近。

【问题讨论】:

我相信你的意思是SSE/AVX,因为MMX只支持64位向量寄存器。 MMX 是过时的 64 位向量,例如 __m64 内在函数。 __m128i 是 SSE/AVX/AVX512 可以使用的最窄。 _mm512_i32gather_epi32 是 AVX512。如果您使用的是 AVX512 数据集,则在 xmm0 等 XMM 寄存器和 mm0 等 MMX 寄存器之间反弹数据是没有用的。可能你想要AVX512F __m128i _mm512_cvtepi32_epi8(__m512i),如果你确定你可以安全地读取你真正想要的每个字节末尾的 3 个字节。 相关:AVX2 byte gather with uint16 indices, into a __m256i 查看使用标量插入手动收集的性能,并讨论但未显示 vpgatherdd 的代码。 你为什么不接受下面的精彩回答? 【参考方案1】:

您正在寻找的随机播放是 AVX512F 的一部分:_mm512_cvtepi32_epi8 (VPMOVDB)。有趣的事实:如果你愿意,它甚至可以以内存目标存储的形式出现,尽管在 Skylake-avx512 硬件上它并不比正常效率高。 (它确实允许在没有 AVX512BW 的 Xeon Phi 上进行字节屏蔽存储。)

是的,如果您可以安全地读取每个字节元素末尾之后的 3 个字节的垃圾without risk of faulting from touching an unmapped page,dword 收集 + 打包可能是您的最佳选择。特别是如果它们不太可能跨越缓存行或特别是页面边界。如果您的索引对最坏情况的字节位置存在偏差,请考虑以不同方式对齐源数据或执行其他操作。

如果索引中有任何类型的模式,这可以提高手动加载+随机播放的效率,特别是如果单个向量加载可以跨越您想要的多个值。即使只有固定的步幅,也值得考虑循环索引以使用vpinsrb 或其他东西一次插入一个元素,如AVX2 byte gather with uint16 indices, into a __m256i。但是使用最近的硬件 (Skylake) 和宽向量(尤其是 AVX512),聚集非常好,每个时钟可以接近 0.5 个元素。


_mm512_i32gather_epi32 的操作数顺序错误,base 当然需要是指针,而不是标量 uin8_t

__m128i bytegather(uint8_t *base, __m512i offsets)

    __m512i values = _mm512_i32gather_epi32(offsets, base, 1);
    return _mm512_cvtepi32_epi8(values);   // pack with truncation.


对于带有_mm256_i32gather_epi32 的 AVX2 版本,您必须使用不同的随机播放。也许提取高半部分,左移,字混合(vpblendw),所以你想要的所有字节都在一个__m128i中。然后vpshufb (_mm_shuffle_epi8) 把你想要的8个字节打包到寄存器的底部?

在聚集之前从高半部分的索引中减去一两个可以避免需要移位,因此您想要的字节位于 dword 元素中的不同位置。但请注意,这意味着如果 index=0 您是从表格开始之前加载的。因此,如果可能出现段错误,则不能这样做。 (而且这对性能来说可能是个坏主意)。


如果您有多个这样的向量,并希望最终从 4 个偏移向量中构建一个 __m512i 字节,您可以考虑使用 2 输入打包指令(如 _mm512_packs_epi32@ 987654324@) 最终使用 qword shuffle 来修复车道内行为。但是这些包只有饱和版本,没有截断,而且首先从每个输入中清除高垃圾需要额外的指令。

也许最好使用_mm512_permutex2var_epi16 (vpermt2w) 作为第一步,尽管它需要在 Skylake-X 上花费多个 shuffle 微指令,不幸的是即使在 vpermb 是单微指令的 Ice Lake 上也是如此。您需要计算总的 shuffle uops 以从 4 个 __m512i 输入中产生一个 __m512i 并查看哪个最便宜,因为这与使用 _mm512_cvtepi32_epi8 截断到 __m128i 然后建立备份。

【讨论】:

以上是关于给定一个 int 偏移向量,如何使用 AVX512 内在函数收集单个字节?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 avx 指令将 float 向量转换为 short int?

AVX-512CD(冲突检测)与原子变量访问有何不同?

如何编译 TensorFlow 二进制文件以使用 AVX2、AVX512F、FMA?

int64_t 指针转换为 AVX2 内在 _m256i

动态确定恶意 AVX-512 指令的执行位置

有没有办法用avx2自动替换avx512?