PTEST 能否用于测试两个寄存器是否均为零或其他情况?
Can PTEST be used to test if two registers are both zero or some other condition?
除了测试单个寄存器是否全为零之外,SSE4.1 ptest
还能做什么?
您能否结合使用 SF 和 CF 来测试关于两个未知输入寄存器的有用信息?
PTEST 有什么用?您可能认为检查打包比较的结果(如 PCMPEQD 或 CMPPS)会很好,但至少在 Intel CPU 上,
另见 Checking if TWO SSE registers are not both zero without destroying them
不,除非我遗漏了一些聪明的东西,否则带有两个未知寄存器的 ptest
通常无法用于检查关于它们的一些 属性。 (除了明显的东西,你已经想要一个按位与,比如两个位图之间的交集)。
测试两个寄存器是否全为零,或将它们放在一起并针对自身进行 PTEST。
ptest xmm0, xmm1
产生两个结果:
- ZF = 是
xmm0 & xmm1
全零吗?
- CF = 是
(~xmm0) & xmm1
全零吗?
如果第二个向量全为零,则标志完全不依赖于第一个向量中的位。
将 "is-all-zero" 检查视为 AND 和 ANDNOT 结果的 NOT(bitwise horizontal-OR())
可能很有用。但可能不会,因为这对我的大脑来说太多了,无法轻松思考。垂直与然后水平或的序列可能会让你更容易理解为什么 PTEST 没有告诉你太多关于两个未知寄存器的组合,就像整数 TEST 指令一样。
这是 2 位 ptest a,mask
的真相 table。希望这有助于考虑 128b 输入的 0 和 1 的混合。
注意 CF(a,mask) == ZF(~a,mask)
.
a mask ZF CF
00 00 1 1
01 00 1 1
10 00 1 1
11 00 1 1
00 01 1 0
01 01 0 1
10 01 1 0
11 01 0 1
00 10 1 0
01 10 1 0
10 10 0 1
11 10 0 1
00 11 1 0
01 11 0 0
10 11 0 0
11 11 0 1
Intel's intrinsics guide lists 2 interesting intrinsics for it。请注意 args 的命名:a
和 mask
是一个线索,它们告诉您有关 a
的部分由已知的 AND 掩码选择的信息。
_mm_test_mix_ones_zeros (__m128i a, __m128i mask)
: returns (ZF == 0 && CF == 0)
_mm_test_all_zeros (__m128i a, __m128i mask)
: returns ZF
还有更简单命名的版本:
int _mm_testc_si128 (__m128i a, __m128i b)
: returns CF
int _mm_testnzc_si128 (__m128i a, __m128i b)
: returns (ZF == 0 && CF == 0)
int _mm_testz_si128 (__m128i a, __m128i b)
: returns ZF
这些内在函数有 AVX2 __m256i
版本,但指南仅列出了 __m128i
操作数的 all_zeros 和 mix_ones_zeros 备用名称版本。
如果你想从 C 或 C++ 测试一些其他条件,你应该使用具有相同操作数的 testc
和 testz
,并希望你的编译器意识到它只需要做一个PTEST,甚至希望使用单个 JCC、SETCC 或 CMOVCC 来实现您的逻辑。 (我建议检查 asm,至少对于你最关心的编译器。)
请注意 _mm_testz_si128(v, set1(0xff))
始终与 _mm_testz_si128(v,v)
相同,因为这就是 AND 的工作原理。但 CF 结果并非如此。
您可以使用
检查向量是否为全一
bool is_all_ones = _mm_testc_si128(v, _mm_set1_epi8(0xff));
这可能并不比针对全一向量的 PCMPEQB 更快,但代码量更小,然后是通常的 movemask + cmp。它不会避免需要矢量常数。
PTEST 的优点是它不会破坏任何一个输入操作数,即使没有 AVX。
除了测试单个寄存器是否全为零之外,SSE4.1 ptest
还能做什么?
您能否结合使用 SF 和 CF 来测试关于两个未知输入寄存器的有用信息?
PTEST 有什么用?您可能认为检查打包比较的结果(如 PCMPEQD 或 CMPPS)会很好,但至少在 Intel CPU 上,
另见 Checking if TWO SSE registers are not both zero without destroying them
不,除非我遗漏了一些聪明的东西,否则带有两个未知寄存器的 ptest
通常无法用于检查关于它们的一些 属性。 (除了明显的东西,你已经想要一个按位与,比如两个位图之间的交集)。
测试两个寄存器是否全为零,或将它们放在一起并针对自身进行 PTEST。
ptest xmm0, xmm1
产生两个结果:
- ZF = 是
xmm0 & xmm1
全零吗? - CF = 是
(~xmm0) & xmm1
全零吗?
如果第二个向量全为零,则标志完全不依赖于第一个向量中的位。
将 "is-all-zero" 检查视为 AND 和 ANDNOT 结果的 NOT(bitwise horizontal-OR())
可能很有用。但可能不会,因为这对我的大脑来说太多了,无法轻松思考。垂直与然后水平或的序列可能会让你更容易理解为什么 PTEST 没有告诉你太多关于两个未知寄存器的组合,就像整数 TEST 指令一样。
这是 2 位 ptest a,mask
的真相 table。希望这有助于考虑 128b 输入的 0 和 1 的混合。
注意 CF(a,mask) == ZF(~a,mask)
.
a mask ZF CF
00 00 1 1
01 00 1 1
10 00 1 1
11 00 1 1
00 01 1 0
01 01 0 1
10 01 1 0
11 01 0 1
00 10 1 0
01 10 1 0
10 10 0 1
11 10 0 1
00 11 1 0
01 11 0 0
10 11 0 0
11 11 0 1
Intel's intrinsics guide lists 2 interesting intrinsics for it。请注意 args 的命名:a
和 mask
是一个线索,它们告诉您有关 a
的部分由已知的 AND 掩码选择的信息。
_mm_test_mix_ones_zeros (__m128i a, __m128i mask)
: returns(ZF == 0 && CF == 0)
_mm_test_all_zeros (__m128i a, __m128i mask)
: returnsZF
还有更简单命名的版本:
int _mm_testc_si128 (__m128i a, __m128i b)
: returnsCF
int _mm_testnzc_si128 (__m128i a, __m128i b)
: returns(ZF == 0 && CF == 0)
int _mm_testz_si128 (__m128i a, __m128i b)
: returnsZF
这些内在函数有 AVX2 __m256i
版本,但指南仅列出了 __m128i
操作数的 all_zeros 和 mix_ones_zeros 备用名称版本。
如果你想从 C 或 C++ 测试一些其他条件,你应该使用具有相同操作数的 testc
和 testz
,并希望你的编译器意识到它只需要做一个PTEST,甚至希望使用单个 JCC、SETCC 或 CMOVCC 来实现您的逻辑。 (我建议检查 asm,至少对于你最关心的编译器。)
请注意 _mm_testz_si128(v, set1(0xff))
始终与 _mm_testz_si128(v,v)
相同,因为这就是 AND 的工作原理。但 CF 结果并非如此。
您可以使用
检查向量是否为全一bool is_all_ones = _mm_testc_si128(v, _mm_set1_epi8(0xff));
这可能并不比针对全一向量的 PCMPEQB 更快,但代码量更小,然后是通常的 movemask + cmp。它不会避免需要矢量常数。
PTEST 的优点是它不会破坏任何一个输入操作数,即使没有 AVX。