首页 > 解决方案 > 使用 AVX512 将压缩 64 位整数转换为带符号饱和的压缩 8 位整数

问题描述

我正在寻找一种将压缩的 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)。

标签: cintrinsicsavxavx512

解决方案


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

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

(正如评论中所讨论的,对于 8x8 => 8 位饱和乘法,您可能只希望在车道内 unpack 中提供,并使用车道内( )_mm256_mullo_epi16打包成对的结果。虽然符号扩展车道内 unpack不方便,所以你可能会考虑。无论哪种方式,你绝对不需要扩大超过 16 位的元素;可以容纳两个的完整乘积而不会溢出。)_mm256_packs_epi16vpacksswbvpmovsxint16_tint8_t


或者按照您要求的方式进行操作,AVX512 确实具有下转换指令的有符号和无符号饱和版本,以及您找到的截断版本。VPMOVQB, VPMOVSQB, 和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 位向量。


推荐阅读