在 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
)不敏感。
英特尔选择的内在函数名称可能看起来像是专门用于 有符号 整数,但它们总是使用 epi
或 si
来表示有效的东西同样在有符号和无符号元素上。但是不,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
.
我想使用 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
)不敏感。
英特尔选择的内在函数名称可能看起来像是专门用于 有符号 整数,但它们总是使用 epi
或 si
来表示有效的东西同样在有符号和无符号元素上。但是不,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
.