我正在尝试使用 SSE 指令进行一些图像过滤。我正在使用的图像每个像素有一个字节(255 灰度),我需要使用大于比较来比较无符号打包字节。我查看了英特尔的手册,并且存在比较,但仅适用于有符号字节(PCMPGTB)。我怎样才能对无符号字节进行比较?提前致谢
4 回答
在 AVX-512 1之前,确实不可能直接进行无符号比较。
但是您可以将 -128 添加到每个值(或减去 128,或 XOR 0x80,或类似的)。这会将 0 变为 -128,将 255 变为 127,其他值变为介于两者之间的值;结果是您从比较中获得了正确的结果。
将其扩展为单词也应该有效,但听起来有点慢,因为您完成了每条指令的一半工作。
_mm_cmpgt_epu8(a, b) = _mm_cmpgt_epi8(
_mm_xor_epi8(a, _mm_set1_epi8(-128)), // range-shift to unsigned
_mm_xor_epi8(b, _mm_set1_epi8(-128)))
pxor
可以在比某些 CPU 上更多的执行端口上运行paddb
,因此如果您需要这样做,它通常是最佳选择。XOR 是无进位加法,加减 0x80 的进位输出到每个字节元素的顶部。
脚注 1:使用 AVX-512BW:
vpcmpub
它将比较谓词作为立即数,例如cmpps
. _mm_cmp[eq|ge|gt|le|lt|neq]_epu8_mask
比较到掩码而不是另一个向量,因为这就是 AVX-512 比较指令的工作方式。例如
__mmask16 _mm_cmpgt_epu8_mask (__m128i a, __m128i b)
在英特尔的内在指南中
无符号比较 (a >= b) 与 maxu( a, b ) == a 相同,因此您可以使用
_mm_cmpeq_epi8( a, _mm_max_epu8(a,b)) --> a >= b "cmpge_epu8(a,b)"
如果您需要一个 <
或>
比较,您需要反转结果,此时 Alcaro 的方法可能同样好(尽管该方法需要一个寄存器来携带一个用于反转的常数)。但是对于 a >=
or<=
比较,这肯定更好(因为没有 _mm_cmple_epi8 或 _mm_cmpge_epi8 可供使用,即使在将无符号转换为有符号范围之后)。
对@greggo 的解决方案提出一个小而重要的改进:
maxu( a, b ) == a
有一个缺点,因为您必须在 maxu 比较之前备份“a”,从而导致补充操作,如下所示:
movq mmc, mma
pmaxu mma, mmb
pcmpeq mma, mmc
这
minu( a, b ) == b
给出完全相同的效果,但保留了相等检查的运算符:
pminu mma, mmb
pcmpeq mma, mmb
收益是显着的:只需 2 次操作而不是 3 次。
除了比较打包的无符号字节之外,不可能做得更好,我已经将字节解包为单词(因为它们是肯定的,就像从无符号到有符号的转换以及从字节到单词的扩展)并使用 PCMPGTB 比较它们。