给定一个 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?