我有 16 字节的“字符串”(它们可能更短,但您可能会假设它们在末尾用零填充),但您可能不会假设它们是 16 字节对齐的(至少并非总是如此)。
如何编写一个例程将它们(相等)与 SSE 内在函数进行比较?我发现这个代码片段可能会有所帮助,但我不确定它是否合适?
register __m128i xmm0, xmm1;
register unsigned int eax;
xmm0 = _mm_load_epi128((__m128i*)(a));
xmm1 = _mm_load_epi128((__m128i*)(b));
xmm0 = _mm_cmpeq_epi8(xmm0, xmm1);
eax = _mm_movemask_epi8(xmm0);
if(eax==0xffff) //equal
else //not equal
有人可以解释这个或写一个函数体吗?
它需要在 GCC/mingw(在 32 位 Windows 上)中工作。
最佳答案
vector 比较指令根据相应源元素之间的比较,将其结果作为掩码,由全 1(真)或全 0(假)的元素组成。
请参阅 https://stackoverflow.com/tags/x86/info 以获取一些链接,这些链接将告诉您这些内在函数的作用。
问题中的代码看起来应该可以工作。
如果您想找出哪些元素不相等,请使用 movemask 版本( pmovmskb
或 movmskps
)。您可以 tzcnt
/bsf
对第一次匹配进行位扫描,或 popcnt
对匹配进行计数。 All-equal 给你一个 0xffff
掩码,non-equal 给你 0
。
您可能想知道 SSE4.1 ptest
在这里是否有用。它可用,但实际上并没有更快,特别是如果您对结果进行分支而不是将其转换为 bool 值 0/-1。
// slower alternative using SSE4.1 ptest
__m128i avec, bvec;
avec = _mm_loadu_si128((__m128i*)(a));
bvec = _mm_loadu_si128((__m128i*)(b));
__m128i diff = _mm_xor_si128(avec, bvec); // XOR: all zero only if *a==*b
if(_mm_test_all_zeros(diff, diff)) { //equal
} else { //not equal
}
在 asm 中,
ptest
是 2 uops,并且不能与 jcc
条件分支进行宏融合。因此,前端的总 pxor
+ ptest
+ branch 是 4 uops,并且仍然会破坏其中一个输入,除非您有 AVX 将异或结果放入第三个寄存器。pcmpeqb xmm0, xmm1
/pmovmskb eax, xmm0
/cmp/jcc
总共是 3 uop,在 Intel 和 AMD CPU 上,cmp/jcc 融合为 1 uop。如果您有更宽的元素,您可以对
movmskps
的结果使用 movmskpd
或 pcmpeqd/q
以获得 4 位或 2 位掩码。如果您想在不为每个元素除以 4 或 8 个字节的情况下进行位扫描或 popcnt,这将非常有用。 (或者使用 AVX2,8 位或 4 位而不是 32 位掩码。)如果您不需要任何额外的指令来为其构建输入,那么
ptest
只是一个好主意:测试是否为全零,带或不带掩码。例如检查每个元素或某些元素中的某些位。
关于将 16 字节字符串与 SSE 进行比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31999284/