我有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上)工作。
答案 0 :(得分:1)
好吧,我不确定这是否会更快,但可以使用单个SSE 4.2指令完成:检查进位和/或溢出标志的PCMPISTRI(打包比较隐式长度字符串,返回索引):
if (_mm_cmpistrc(a, b, mode)) // checks the carry flag (not set = equal)
// equal
else
// unequal
模式将是(对于您的情况):
const int mode =
SIDD_UBYTE_OPS | // 16-bytes per xmm
SIDD_CMP_EQUAL_EACH | // strcmp
SIDD_NEGATIVE_POLARITY; // find first different byte
不幸的是,这条指令的记录很少。 因此,如果有人发现聚合所有模式组合和结果标志的合适资源,请分享。
答案 1 :(得分:0)
我会尝试帮助被遗忘的有人解释这个部分问题。
register __m128i xmm0, xmm1;
register unsigned int eax;
这里我们声明一些变量。 __m128i
是用于SSE寄存器上的整数运算的内置类型。请注意,变量的名称根本不重要,但作者已将它们命名为在汇编中调用相应的CPU寄存器。 xmm0
,xmm1
,xmm2
,xmm3
,...是SSE操作的所有寄存器。 eax
是通用寄存器之一。
register
关键字来建议编译器将变量放在CPU寄存器中。我认为今天完全没用。有关详细信息,请参阅this question。
xmm0 = _mm_loadu_si128((__m128i*)(a));
xmm1 = _mm_loadu_si128((__m128i*)(b));
此代码已修改为@harold建议。这里我们从给定的内存指针(可能是未对齐的)加载16个字节到变量xmm0
和xmm1
。在汇编代码中,这些变量很可能直接位于寄存器中,因此这种内在函数会产生未对齐的内存负载。将指针转换为__m128i*
类型是必要的,因为内部接受此指针类型,但我不知道为什么英特尔会这样做。
xmm0 = _mm_cmpeq_epi8(xmm0, xmm1);
这里我们比较xmm0
变量中每个字节与xmm1
变量中相应字节的相等性。后缀_epi8
表示对8位元素(即字节)进行操作。它有点类似于memcmp(&xmm0, &xmm1, 16)
,但会产生其他结果。它返回一个16字节的值,对于每个具有相等值的字节,包含0xFF
,对于具有不同值的每个字节,包含0x00
。
eax = _mm_movemask_epi8(xmm0);
这是来自SSE2的非常重要的指令,它通常用于编写具有某些SSE条件的if
语句。它从XMM参数中的16个字节中的每个字节获取最高位,并将它们写入单个16位整数。在汇编级别,此编号位于通用寄存器中,允许我们在之后快速检查其值。
if(eax==0xffff) //equal
else //not equal
如果两个XMM寄存器的所有16个字节都相等,那么_mm_cmpeq_epi8
必须返回一个设置了所有128位的掩码。然后,_mm_movemask_epi8
将返回完整的16位掩码,即0xFFFF
。如果任何两个比较字节不同,相应的字节将由_mm_cmpeq_epi8
填充零,因此_mm_movemask_epi8
将返回16位掩码,相应的位不设置,所以它会小于0xFFFF
。
此外,以下是包含在函数中的解释代码:
bool AreEqual(const char *a, const char *b) {
__m128i xmm0, xmm1;
unsigned int eax;
xmm0 = _mm_loadu_si128((__m128i*)(a));
xmm1 = _mm_loadu_si128((__m128i*)(b));
xmm0 = _mm_cmpeq_epi8(xmm0, xmm1);
eax = _mm_movemask_epi8(xmm0);
return (eax == 0xffff); //equal
}