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

Converting packed 64-bit integers to packed 8-bit integers with signed saturation using AVX512

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

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

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

(正如评论中所讨论的那样,对于 8x8 => 8 位饱和乘法,您可能只想 in-lane 解包以提供 _mm256_mullo_epi16,然后将成对的结果用 in-lane _mm256_packs_epi16 (vpacksswb)。虽然sign-extending in-lane 解包不方便所以你可以考虑vpmovsx。不管怎样,你肯定不需要加宽超过 16 位的元素;int16_t 可以容纳两个 int8_t 的完整乘积而不会溢出。)


或者按照您的要求进行操作,AVX512 确实具有 down-convert 指令的有符号和无符号饱和版本,以及您找到的截断版本。 VPMOVQB, VPMOVSQB, and VPMOVUSQB都一起记录了。

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

(商店版本在主流 CPU 上效率不高,但它确实允许 KNL / KNM(缺少 AVX512BW)进行狭窄的 byte-masked 商店。)


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


另一种选择是打包 2 个向量 -> 1 个与 2 个输入宽度相同的向量,但它们仅适用于 in-lane 打包指令。例如_mm256_packs_epi16 如上所述。它们仅适用于 2:1 元素大小比率,而不是一步从 64 或 32 一直到 8。 (这是避免扩大太多的另一个原因)。

但是如果您查看产生 N 字节输出数据的混洗总数,它往往会略微领先。例如对于 4 个输入向量,您需要 2 + 1 次洗牌而不是 4 次洗牌才能从 32 位缩小到 8 位。 (如果您需要修复 in-lane,如果您无法在 128 位通道中向它们提供数据交错 odd/even 的指令,则可能是第 4 次洗牌)。您必须查看解压缩以及 re-pack.

需要多少次随机播放(或可能的其他指令,如 AND 或 AVX512 byte-masking)的大图

2:1 如果您要存储结果,打包的优点是可以扩大存储范围。如果不是,那么与新的 AVX512 1->1 向量缩小指令相比,这是一个更大的优势,在那里你需要洗牌将它们重新组合成一个 256 位向量。