除了测试单个寄存器是否全为零之外,您还可以使用SSE4.1 ptest
做什么?
您可以使用SF和CF的组合来测试有关两个未知输入寄存器的任何有用信息吗?
PTEST有什么用?您认为检查打包比较的结果(如PCMPEQD或CMPPS)会有好处,但至少在Intel CPU上it costs more uops to compare-and-branch using PTEST + JCC than with PMOVMSK(B/PS/PD) + macro-fused CMP+JCC.
另见Checking if TWO SSE registers are not both zero without destroying them
答案 0 :(得分:5)
不,除非我遗漏了一些聪明的东西,ptest
有两个未知的寄存器通常对检查两者的某些属性没有用。 (除了明显的东西,你已经想要一个按位-AND,比如两个位图之间的交点)。
测试两个寄存器的全为零,或者它们在一起,而PTEST则反对自身。
ptest xmm0, xmm1
会产生两个结果:
xmm0 & xmm1
全零?(~xmm0) & xmm1
全零?如果第二个向量全为零,则标志根本不依赖于第一个向量中的位。
将“is-all-zero”检查视为AND和ANDNOT结果的NOT(bitwise horizontal-OR())
可能会有用。但可能不是,因为这是我的大脑很容易思考的步骤。这个垂直AND和然后水平OR的序列可能更容易理解为什么PTEST不会告诉你很多关于两个未知寄存器的组合,就像整数TEST指令一样。
这是一个2位ptest a,mask
的真值表。希望这有助于考虑零和一对128b输入的混合。
请注意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
是他们告诉您已知AND掩码选择的a
部分的线索。
_mm_test_mix_ones_zeros (__m128i a, __m128i mask)
:返回(ZF == 0 && CF == 0)
_mm_test_all_zeros (__m128i a, __m128i mask)
:返回ZF
还有更简单命名的版本:
int _mm_testc_si128 (__m128i a, __m128i b)
:返回CF
int _mm_testnzc_si128 (__m128i a, __m128i b)
:返回(ZF == 0 && CF == 0)
int _mm_testz_si128 (__m128i a, __m128i b)
:返回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,它也不会破坏任何输入操作数。