使用 AVX512 将压缩 64 位整数转换为带符号饱和的压缩 8 位整数

Posted

技术标签:

【中文标题】使用 AVX512 将压缩 64 位整数转换为带符号饱和的压缩 8 位整数【英文标题】:Converting packed 64-bit integers to packed 8-bit integers with signed saturation using AVX512 【发布时间】:2020-01-30 08:30:04 【问题描述】:

我正在寻找一种将压缩的 64 位整数饱和到 8 位整数的解决方案。查看_mm256_cvtepi64_epi8,但不是饱和,而是截断,导致不需要的输出。

我的程序如下:

int main()

    __m256i a, b, c;
    __m128i d;

    a = _mm256_set1_epi64x(127);
    b = _mm256_set1_epi64x(1);
    c = _mm256_add_epi64x(a, b);
    d = _mm256_cvtepi64_epi8(c);

我希望输出 (d) 包含四个 127(饱和),但是程序会产生四个 -128 元素(从 128 截断)。

【问题讨论】:

【参考方案1】:

_mm256_cvtepi64_epi8 是 AVX512。 (特别是 AVX512VL;512 位版本是 AVX512F)。您标记了它,但您的(原始)标题只说 AVX。

无论如何,您的选择包括首先使用 _mm256_adds_epi8 进行饱和加法,这样每个向量可以有 8 倍的元素。

(正如在 cmets 中所讨论的,对于 8x8 => 8 位饱和乘法,您可能只希望在通道内解包以提供 _mm256_mullo_epi16,并使用通道内 _mm256_packs_epi16(@ 987654330@)。虽然符号扩展in-lane unpack不方便,所以你可以考虑vpmovsx。无论如何,你绝对不需要扩大超过16位的元素;int16_t可以容纳完整的产品两个int8_t 没有溢出。)


或者按照您要求的方式进行操作,AVX512 确实具有下转换指令的有符号和无符号饱和版本,以及您找到的截断版本。 VPMOVQB, VPMOVSQB, and VPMOVUSQB 全部记录在一起。

__m128i _mm256_cvtsepi64_epi8(__m256i a); 进行有符号饱和。它有一个带有__m512i 源的版本,以及一个直接存储到内存的版本(可选作为掩码存储)。

(存储版本在主流 CPU 上效率不高,但它确实允许 KNL / KNM(缺少 AVX512BW)进行窄字节屏蔽存储。)


除非必要,否则不要将数据扩展到 64 位元素。与 8 位元素相比,这是每个向量工作量的 1/8,并且自 Haswell 以来,32x32 => 32 位和 64x64 => 64 位 SIMD 乘法在 Intel 上每条指令需要 2 微秒。


另一种选择是打包 2 个向量 -> 1 个与 2 个输入具有相同宽度的向量,但它们仅适用于通道内打包指令。例如_mm256_packs_epi16 如上所述。它们仅适用于 2:1 的元素尺寸比,而不是一步从 64 或 32 到 8。 (所以还有一个避免过度扩大的原因)。

但是,如果您查看生成 N 字节输出数据的 shuffle 总数,它往往会略微领先。例如对于 4 个输入向量,您需要 2 + 1 次洗牌而不是 4 次,以从 32 位缩小到 8 位。 (如果您需要在通道内修复,如果您无法在 128 位通道中向它们提供奇数/偶数交错数据的指令,则可能是第 4 次洗牌)。您必须全面了解解包和重新打包需要多少次随机播放(或可能的其他指令,如 AND 或 AVX512 字节掩码)。

2:1 包装的优势在于,如果您甚至存储结果,则可以扩大商店。如果不是,那么这比新的 AVX512 1->1 向量缩小指令具有更大的优势,在这种指令中,您需要洗牌才能将它们重新组合成 256 位向量。

【讨论】:

@thnghh:有 16 位和 32 位乘法指令。如果您的输入足够窄,您可以使用它而不是扩大到 64 并饱和回落到 8。甚至还有 vpmaddubsw 进行水平累积;您可以在对一个输入进行奇数/偶数屏蔽之后使用它,或者某种形式的通道内解包将vpacksswb 反馈回字节。 SIMD 经验法则:尽可能少地扩大以保持高数据密度。 (熵小的向量通常是一个不好的迹象) 第一个解决方案对于加法情况(a+b)非常好。但是,有时我必须解决乘法情况 (a*b)。例如:a 包含八个 50b 包含八个 40c = _mm256_mul_epi32(a, b) 产生四个 200,我想要 d包含四个127c的饱和版本) @thnghh:在这种情况下,您只需要 16 位输入来提供 vpmullw -> vpacksswb。 (传统的 2:1 饱和包指令在通道内,但您只需要每 2 个向量 1 个,而不是使用 AVX512 shuffle 时每个向量 1 个。尽可能使用 whem。 @thnghh:你错过了我最后的评论吗? vpmullw 是 16x16 => 16 位乘法,因此您只需将输入扩展那么多,而不是 32 位!或者我的第一条评论提到了一种将vpmaddubsw 用作 8x8 => 16 位扩展乘法的方法(但前提是您可以将一个输入视为有符号,而将另一个视为无符号)。可以在Intel's intrisincs finder中搜索指令助记词 @thnghh: felixcloutier.com/x86 是英特尔第 2 卷 PDF 手册的摘录。但是,如果您知道英特尔用来描述事物的术语,那么英特尔的内在函数查找器确实非常好。或者要搜索 asm 助记符,您总是可以将它们作为搜索词放入内在函数查找器中并获取内在文档(并不总是像 asm 手册那样详细)。 AVX512 指令的所有屏蔽版本使内部函数查找器变得非常臃肿。考虑在搜索之前取消选中 AVX512 框,或者只是 AVX512VL 框

以上是关于使用 AVX512 将压缩 64 位整数转换为带符号饱和的压缩 8 位整数的主要内容,如果未能解决你的问题,请参考以下文章

AVX 将 64 位整数转换为 64 位浮点数

使用 AVX512 或 AVX2 计算所有压缩 32 位整数之和的最快方法

使用 intel 内在函数将压缩的 8 位整数乘以浮点向量

AVX512BW:使用bsf / tzcnt处理32位代码中的64位掩码?

如何在 AVX 中使用融合乘法和加法来处理 16 位压缩整数

AVX2 1x mm256i 32bit 到 2x mm256i 64bit