问题 __m128i变量是零吗?


如何测试a __m128i 变量在SSE-2和更早的处理器上有任何非零值?


3036
2017-11-03 03:18


起源

你的意思是非零位,或8/16/32位整数元素? - Brett Hale
@BrettHale:我正在测试他们是否都是零。 - Mehrdad


答案:


在SSE2中,您可以:

__m128i zero = _mm_setzero_si128();
if(_mm_movemask_epi8(_mm_cmpeq_epi32(x,zero)) == 0xFFFF)
{
    //the code...
}

这将测试四个int与零然后为每个字节返回一个掩码,因此每个对应的位偏移 int 将在0,4,8和12,但上述测试将捕获是否设置任何位,然后如果您保留掩码,您可以直接使用更细粒度的部分,如果需要。


11
2017-11-03 06:37



+1,它比我的好。 :)我从来没有使用过movemask指令,所以我不知道你能做到这一点。 XD - Mysticial
我见过+1最紧凑的解决方案,谢谢! - Mehrdad
在其他优秀的答案中有一个错误 - 如果你要检查所有的零,它应该是 if(_mm_movemask_epi8(_mm_cmpeq_epi32(x,zero)) == 0xFFFF)。这是因为 _mm_cmpeq_epi32 将int设置为全1,而不是全0,如果它等于零,然后是 _mm_movemask_epi8 根据参数中每个字节的最高有效位设置前16位。希望作者可以编辑答案 - 我试过但被拒绝了。 - FarmerBob
我以不同的方式阅读原始问题。您的代码执行您所说的操作,即检查所有四个32位值是否为非零。我将问题解释为“任何”值是非零,如问题正文中所述,或相反,如果它们都是零,正如问题的标题和OP对Brett Hale的澄清一样。如果这就是所需要的(这是我的项目所需要的,这导致我找到这个问题),那么你需要针对0xFFFF进行测试。 - FarmerBob
@LeonidTsybert:我可以更新掩码,但TBH如果你无法阅读关于代码的注释,你不应该接触SIMD内容... - Necrolis


为了完整起见,可以使用SSE4 _mm_testz_si128

const bool isAllZero = _mm_testz_si128(a,a);

请注意,这是 真正 什么时候 所有位都为零


2
2018-03-09 11:47



这实际上稍微快一点,并且不需要全零寄存器来测试。 ptest / jz 是2 + 1 uop(不是宏观融合)。 pcmpeq(1uop)/ pmovmsk(1uop)/ and 0xffff (1uop)/ cmp 0xffff/je (1uop)。如果你正在测试另一个案例(任何 零元素,而不是 所有 零元素),它们在当前的Intel和AMD CPU上的性能大致相同: ptest/jnz (3次) pcmpeq / pmovmsk / test/jnz (3次)。 - Peter Cordes
@PeterCordes在这种情况下,在所有的寄存器中设置寄存器并使用 _mm_testc_si128?就像是 const bool atLeastOneZero = _mm_testc_si128(a,allOnes) - Antonio
再次, ptest 稍快一些。没有这样做 ptest, 你 pcmpeq 对于全1向量,然后继续完全相同的序列以检查所有元素是否匹配。检查全零或全部 pcmpeq 与检查==任何其他模式相同,除了常量更容易生成(pxor same,same 要么 pcmpeqw same,same)。 - Peter Cordes