8

除了测试单个寄存器是否全零之外,您还能用SSE4.1做什么?ptest

你能结合使用 SF 和 CF 来测试关于两个未知输入寄存器的有用信息吗?

PTEST 有什么用?您会认为检查打包比较的结果(如 PCMPEQD 或 CMPPS)会很好,但至少在英特尔 CPU 上,使用 PTEST + JCC 进行比较和分支的成本比使用 PMOVMSK(B /PS/PD) + 宏融合 CMP+JCC。

另请参阅检查两个 SSE 寄存器是否不都为零而不破坏它们

4

1 回答 1

5

不,除非我错过了一些聪明的东西,否则ptest两个未知的寄存器通常对于检查它们的某些属性没有用。(除了你已经想要一个按位与的明显的东西,比如两个位图之间的交集)。

测试两个寄存器是否都为零,或者将它们放在一起,然后对它自己进行 PTEST 测试。


ptest xmm0, xmm1产生两个结果:

  • ZF = 是xmm0 & xmm1全零吗?
  • CF = 是(~xmm0) & xmm1全零吗?

如果第二个向量全为零,则标志完全不依赖于第一个向量中的位。

将“全为零”检查视为NOT(bitwise horizontal-OR())AND 和 ANDNOT 结果的一个可能很有用。但可能不会,因为这对我的大脑来说太多了,无法轻松思考。垂直与然后水平或的序列可能会让您更容易理解为什么 PTEST 没有告诉您太多关于两个未知寄存器组合的信息,就像整数 TEST 指令一样。

这是 2-bit 的真值表ptest a,mask。希望这有助于思考具有 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

英特尔的内在函数指南列出了 2 个有趣的内在函数a请注意 args:的命名,这mask是它们告诉您a由已知 AND 掩码选择的部分的线索。

  • _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版本,但该指南仅列出了操作数的 all_zeros 和 mix_ones_zeros 备用名称版本__m128i

如果您想测试 C 或 C++ 的其他条件,您应该使用testctestz与相同的操作数,并希望您的编译器意识到它只需要执行一次 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。

于 2017-04-30T23:03:25.030 回答