在 16 位添加 AVX2 时溢出而不是饱和

overflow instead of saturation on 16bit add AVX2

我想使用 AVX2 添加 2 个无符号向量

__m256i i1 = _mm256_loadu_si256((__m256i *) si1);
__m256i i2 = _mm256_loadu_si256((__m256i *) si2);

__m256i result = _mm256_adds_epu16(i2, i1);

但是我需要溢出而不是饱和 _mm256_adds_epu16 确实与非矢量化代码相同,有什么解决方案吗?

使用正常的二进制包装 _mm256_add_epi16 而不是饱和 adds

二进制补码和无符号addition/subtraction是相同的二进制运算,这也是现代计算机使用二进制补码的原因之一。正如 asm manual entry for vpaddw 提到的,这些指令可用于有符号或无符号整数。 (内在函数指南条目根本没有提到符号,因此对消除这种混淆帮助不大。)

_mm_cmpgt_epi32 这样的比较对符号敏感,但数学运算(和 cmpeq)不敏感。


英特尔选择的内在函数名称可能看起来像是专门用于 有符号 整数,但它们总是使用 episi 来表示有效的东西同样在有符号和无符号元素上。但是不,epu 表示一个特别未签名的东西,而 epi 可以是特别签名的操作,或者可以是对签名或未签名同样有效的东西。或者签名无关紧要的事情。

例如,_mm_and_si128 是纯按位的。 _mm_srli_epi32 是一个 逻辑 右移,向零移动,就像一个无符号的 C 移位。不是符号位的副本,即 _mm_srai_epi32(按立即数右移算术)。 _mm_shuffle_epi32 之类的随机播放只是以块的形式移动数据。

Non-widening 乘法如 _mm_mullo_epi16_mm_mullo_epi32 对于有符号或无符号也是相同的。只有 high-half _mm_mulhi_epu16 或扩大乘法 _mm_mul_epu32 具有未签名的形式作为其特定签名的 epi16/32 形式的对应物。

这也是为什么 386 只添加了一个标量整数 imul ecx, esi 形式,而不是 mul ecx, esi,因为只有 FLAGS 设置会有所不同,而不是整数结果。而且 SIMD 操作甚至没有 FLAGS 输出。

内在函数指南毫无帮助地将 _mm_mullo_epi16 描述为 sign-extending 并生成 32 位产品,然后截断到低 32 位。 asm manual for pmullw 也将其描述为以这种方式签名,似乎在谈论它作为签名 pmulhw 的伴侣。 (并且有一些错误,比如将 AVX1 VPMULLW xmm1, xmm2, xmm3/m128 形式描述为乘以 32 位双字元素,可能是来自 pmulld 的 copy/paste 错误)

有时英特尔的命名方案是有限的,比如 _mm_maddubs_epi16 是 u8 x i8 => 16 位加宽乘法,水平添加对(带符号饱和度)。我通常必须查找 pmaddubsw 的内在函数,以提醒自己他们以输出元素宽度命名它,而不是输入。输入有不同的符号,所以如果他们必须选择一个,边,我想为输出命名它是有意义的,一些输入可能会发生带符号的饱和,比如 pmaddwd.