我试图找到使用 SSE(最高 SSE 4.2)执行 8 位无符号比较的最佳方法。
我正在处理的最常见情况是比较 > 0U,例如
_mm_cmpgt_epu8(v, _mm_setzero_si128()) // #1
(当然,这也可以被认为是对非零的简单测试。)
但我也对更一般的情况感兴趣,例如
_mm_cmpgt_epu8(v1, v2) // #2
第一种情况可以使用 2 条指令,使用各种不同的方法来实现,例如与 0 比较,然后将结果取反。第二种情况通常需要 3 条指令,例如从两个操作数中减去 128 并执行有符号比较。 (看这个问题 https://stackoverflow.com/questions/32945410/sse2-intrinsics-comparing-unsigned-integers适用于各种 3 种指令解决方案。)
我正在寻找的理想情况是#1 的单指令解决方案和#2 的双指令解决方案。如果这两者都不可能,那么我也对各种可能的 2 或 3 指令实现中哪一种在现代 Intel CPU(Sandy Bridge、Ivy Bridge、Haswell)上最有效的想法感兴趣。
到目前为止案例 #2 的最佳实现:
#define _mm_cmpgt_epu8(v0, v1) \
_mm_andnot_si128(_mm_cmpeq_epi8(_mm_max_epu8(v0, v1), v1), \
_mm_set1_epi8(-1))
两条算术指令 + 一条按位指令 = 1.33 吞吐量。
- 反转两个参数的符号位(== 减去 128)并使用有符号比较:
#define _mm_cmpgt_epu8(v0, v1) \
_mm_cmpgt_epi8(_mm_xor_si128(v0, _mm_set1_epi8(-128)), \
_mm_xor_si128(v1, _mm_set1_epi8(-128)))
一条算术指令 + 两条按位 = 1.16 吞吐量。
案例 #1 的最佳实现,源自上面的案例 #2 实现:
#define _mm_cmpgtz_epu8(v0) \
_mm_andnot_si128(_mm_cmpeq_epi8(v0, _mm_set1_epi8(0)), \
_mm_set1_epi8(-1))
一条算术指令 + 一位按位 = 0.83 吞吐量。
#define _mm_cmpgtz_epu8(v0) \
_mm_cmpgt_epi8(_mm_xor_si128(v0, _mm_set1_epi8(-128)), \
_mm_set1_epi8(-128)))
一条算术指令 + 一位按位 = 0.83 吞吐量。