SSE2 直接测试 xmm 位掩码而不使用“pmovmskb”

Posted

技术标签:

【中文标题】SSE2 直接测试 xmm 位掩码而不使用“pmovmskb”【英文标题】:SSE2 test xmm bitmask directly without using 'pmovmskb' 【发布时间】:2020-02-28 07:01:14 【问题描述】:

考虑我们有这个:

....
pxor            xmm1, xmm1
movdqu          xmm0, [reax]
pcmpeqb         xmm0, xmm1
pmovmskb        eax,  xmm0
test            ax , ax
jz              .zero
...

有什么办法不使用'pmovmskb'并直接从xmm0测试位掩码(检查它是否为零)? 此操作是否有任何 SSE 指令?

事实上,我正在搜索类似 'ptest xmm0, xmm0' 的操作,但在 SSE2 中...而不是 SSE4

【问题讨论】:

请注意,由于pcmpeqb 在相等时将字段设置为0xff,因此您需要cmp ax, 0xffffcmp eax, 0xffff 而不是test ax, ax 我已经投票结束这个问题,因为“需要更清楚”,因为不清楚您是否要检查 xmm0 中的任何字节是否为零,或者它们是否全部为零。请澄清这一点,我将撤回我的投票。 您的代码正在检查xmm0 中是否有任何非零字节,而不是xmm0 是否全为零。你能澄清一下这是否是你想要的吗?也许还说那个测试的上下文是什么? (它真的很关键还是你在​​进行微优化?) 如果您确实在寻找类似ptest 指令的东西,那么您拥有的序列已经是最佳选择(在您使用cmp eax, 0xffff 而不是test ax, ax 之后)。这已经被问过很多次了。此外,如果您希望其他人注意到您更改了某些内容,请在评论中注明您要回复的人的 @-mention。 如果您想检查内存中的 128 个连续位是否全为零,您最好使用mov rdx, [rax]; or rdx, [rax+8]; je .zero; 之类的东西。但如上所述,您需要展示更多关于您实际想要实现的内容... 【参考方案1】:

通常不值得在 pcmpeqb 结果上使用 SSE4.1 ptest xmm0,xmm0,尤其是在您进行分支时。

pmovmskb 是 1 uop,cmptest 可以与 jnz 宏融合到 Intel 和 AMD CPU 上的另一个单个 uop。 使用 pmovmsk + test/jcc 对 pcmpeqb 结果进行分支总共需要 2 个微指令

但是ptest 是 2 微指令,它的第 2 微指令 不能 与下面的分支进行宏融合。 在带有ptest + jcc 的向量上分支总共需要 3 微指令


当您可以直接使用 ptest 而不需要 pcmp 时,这是收支平衡的,例如测试整个向量中的任何/所有位(或使用掩码,一些位)。如果您将其用于 cmov 或 setcc 而不是分支,则实际上是一种胜利。即使微指令数量相同,这也是代码大小的胜利。


您可以分摊检查多个向量。 例如por 一些向量在一起,然后检查所有字节是否为零。或者pminub 一些向量在一起,然后检查 any 零。 (像 strlen 和 strchr 这样的 glibc 字符串函数使用这个技巧来并行检查整个缓存行的向量,然后在离开循环后对它的来源进行排序。)

您可以组合 pcmpeq 结果而不是原始输入,例如对于memchr。在这种情况下,您可以使用 pand 而不是 pminubany 输入为零的元素中获取零。一些 CPU 在比 pminub 更多的端口上运行 pand,因此向量 ALU 的竞争更少。


还要注意 pmovmskb 零扩展为 EAX;你可以test eax,eax 而不是浪费一个前缀字节来只测试 AX。

【讨论】:

一个关于'test eax,eax'的问题......所以你告诉我如果我们处于长模式(64位),最好这样做'test rax,rax ' ? @ELHASKSERVERS No. test rax, rax 需要一个前缀字节(正如您在之前的一个问题中已经注意到的那样),而 test eax, eax 不需要。 @ELHASKSERVERS:长模式下的默认操作数大小为 32 位。当您可以选择节省代码大小时使用它(即当它不需要任何额外的指令时),出于同样的原因,您可以使用 xor eax,eax 将 64 位寄存器归零【参考方案2】:

使用ptest:

ptest xmm0, xmm0
jz .zero

ptest a, b 如果ab 为零,则设置 ZF,如果 a ∧ ¬ b 为零,则设置 CF。

但请注意,ptest 需要 SSE 4.1。

否则,我想你的方法是as good as it gets。

【讨论】:

在 SSE2 中还有其他方法吗? @ELHASKSERVERS 据我所知,没有一个比你已经拥有的更好。有关详细信息,请参阅链接的问题。 @ELHASKSERVERS 另见this answer。 @fuz 链接的问题检查全零。上面的问题检查所有字节是否非零。使用ptest(在pcmpeqb之后)似乎是最有效的方法(如果有的话)。 @chtz 我想 OPs 代码是错误的,因为下面的文字清楚地表明他想检查寄存器是否全为零。

以上是关于SSE2 直接测试 xmm 位掩码而不使用“pmovmskb”的主要内容,如果未能解决你的问题,请参考以下文章

PHP中基于位掩码获取数组值

avx512中比较内在指令的不同语义?

使用 SQLite 进行位掩码分组

位运算计算位掩码再枚举——318. 最大单词长度乘积

为啥 MSVC 使用 SSE2 指令来处理这种琐碎的事情?

无法应用位掩码