我想使用 AVX2 添加 2 个 unsigned 向量
__m256i i1 = _mm256_loadu_si256((__m256i *) si1);
__m256i i2 = _mm256_loadu_si256((__m256i *) si2);
__m256i result = _mm256_adds_epu16(i2, i1);
但是我需要溢出而不是饱和, _mm256_adds_epu16
确实与非矢量化代码相同,有什么解决方案吗?
回答1
使用正常的二进制包装 _mm256_add_epi16
而不是饱和 adds
。
二进制补码和 unsigned 加法/减法是相同的二进制运算,这也是现代计算机使用二进制补码的原因之一。正如 vpaddw
的 https://www.felixcloutier.com/x86/paddb:paddw:paddd:paddq
_mm_cmpgt_epi32
之类的比较对符号敏感,但数学运算(和 cmpeq
)不敏感。
Intel 选择的 intrinsics 名称可能看起来像是专门用于有符号整数的,但它们总是使用 epi
或 si
来表示同样适用于有符号和 unsigned 元素的事物。但是不,epu
意味着一个特定的 unsigned 事物,而 epi
可以是专门签名的操作,或者可以是在签名或 unsigned 上同等工作的事物。或者与签名无关的事情。
例如,_mm_and_si128
是纯按位的。 _mm_srli_epi32
是逻辑右移,在零中移位,就像 unsigned C 移位。不是符号位的副本,即 _mm_srai_epi32
(立即右移算术)。像 _mm_shuffle_epi32
这样的随机播放只是以块的形式移动数据。
像 _mm_mullo_epi16
和 _mm_mullo_epi32
这样的非扩展乘法对于有符号或 unsigned 也是相同的。只有高半 _mm_mulhi_epu16
或加宽乘法 _mm_mul_epu32
有 unsigned 形式作为它们专门签名的 epi16
/32
形式的对应物。
这也是为什么 386 只添加了一个标量整数 imul ecx, esi
形式,而不是一个 mul ecx, esi
,因为只有 FLAGS 设置会有所不同,而不是整数结果。 SIMD 操作甚至没有 FLAGS 输出。
intrinsics 指南无益地将 _mm_mullo_epi16
描述为符号扩展并生成 32 位乘积,然后截断为低 32 位。 pmullw
的 https://www.felixcloutier.com/x86/pmullw
有时英特尔的命名方案是有限的,比如 _mm_maddubs_epi16
是一个 u8 x i8 => 16 位加宽乘法,水平添加对(有符号饱和)。我通常必须查找 pmaddubsw
的内在函数,以提醒自己他们以输出元素宽度而不是输入来命名它。输入有不同的符号,所以如果他们必须选择一个,边,我想为输出命名是有意义的,有符号饱和可能发生在某些输入上,比如 pmaddwd
。