SIMD/SSE:如何检查所有向量元素是否非零

11

我需要检查所有向量元素是否都为非零。目前我找到了以下解决方案。有更好的方法吗?我在Linux/x86_64上使用gcc 4.8.2,指令版本是SSE4.2。

typedef char ChrVect __attribute__((vector_size(16), aligned(16)));

inline bool testNonzero(ChrVect vect)
{
    const ChrVect vzero = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
    return (0 == (__int128_t)(vzero == vect));
}

更新:上面的代码编译为以下汇编代码(当作为非内联函数编译时):

movdqa  %xmm0, -24(%rsp)
pxor    %xmm0, %xmm0
pcmpeqb -24(%rsp), %xmm0
movdqa  %xmm0, -24(%rsp)
movq    -24(%rsp), %rax
orq -16(%rsp), %rax
sete    %al
ret

你检查过它生成的代码了吗? - Paul R
1
@i486:这只是一种编码风格,当你把常量放在前面时,如果你不小心使用 = 而不是 ==,编译器会报错。 - Daniel Frużyński
1
你的代码与你的问题不符。你的问题想知道所有元素是否都是非零的。但是你的代码检查的是任何元素是否为非零。 - Raymond Chen
1
@RaymondChen:这里有两个比较。第一个比较输入向量和零向量,然后创建一个新向量,其中包含各个元素的比较结果。当它们全部为非零时,结果向量将是一个零向量。将其转换为__int128_t值后,值也将为零,并返回true。 - Daniel Frużyński
https://godbolt.org/g/Ytz8gg - Z boson
显示剩余8条评论
1个回答

8

使用SSE指令集,您可以像这样完成:

inline bool testNonzero(__m128i v)
{
    __m128i vcmp = _mm_cmpeq_epi8(v, _mm_setzero_si128());
#if __SSE4_1__  // for SSE 4.1 and later use PTEST
    return _mm_testz_si128(vcmp, vcmp);
#else           // for older SSE use PMOVMSKB
    uint32_t mask = _mm_movemask_epi8(vcmp);
    return (mask == 0);
#endif
}

我建议您查看编译器当前为现有代码生成的内容,然后将其与使用内置函数的此版本进行比较,看是否存在显着差异。

使用SSE3 (clang -O3 -msse3),我得到了上述函数的以下结果:

pxor    %xmm1, %xmm1
pcmpeqb %xmm1, %xmm0
pmovmskb    %xmm0, %ecx
testl   %ecx, %ecx

SSE4版本(clang -O3 -msse4.1)的输出结果如下:

pxor    %xmm1, %xmm1
pcmpeqb %xmm1, %xmm0
ptest   %xmm0, %xmm0

请注意,对于包含此函数的任何循环而言,xmm1 的清零通常会被提升出来。因此,在循环内使用上述序列时,应将指令减少一个。

1
谢谢。我试着将它与我的原始代码进行基准测试,结果表明它更快了。当我将其用作非内联函数时,速度提升很小,约为2%。当函数内联时,您的版本比我的快了约26%。 - Daniel Frużyński
@DanielFrużyński:你可能想尝试上面更新的版本,其中包含哈罗德建议的更改-我认为这可能会在某些CPU上节省一个延迟周期。 - Paul R
1
这里使用 ptest 有用吗? - Mysticial
1
@PaulR 我已经做过这个了,两个版本的结果几乎相同。 - Daniel Frużyński
2
@Mysticial:即使ptest可以节省一条指令,但它似乎并不总是胜利的 - 无论如何,我已经将其作为SSE4及更高版本的替代选择添加了,只是为了完整性。 - Paul R
3
如果ptest指令是单uop指令,那将会很好,但事实并非如此。ptest / setcc总共有三个uops。pmovmskb / test / setcc也是三个uops。pmovmskb / test/jcc只有两个uops,因为test/jcc可以宏融合(Intel和AMD)。我在实际微基准测试中没有进行过太多关于ptest的实验,但至少在静态分析中,并不能通过使用它来处理pcmp*结果。它最有可能在处理两个不同的输入时有用,而不是测试某个东西与其自身相比。无论如何,我建议不要使用它来处理此问题,即使它可用。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接