将16字节字符串与SSE进行比较

时间:2015-08-13 22:11:04

标签: c gcc x86 sse simd

我有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上)工作。

2 个答案:

答案 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寄存器。 xmm0xmm1xmm2xmm3,...是SSE操作的所有寄存器。 eax是通用寄存器之一。

很久以前使用

register关键字来建议编译器将变量放在CPU寄存器中。我认为今天完全没用。有关详细信息,请参阅this question

xmm0 = _mm_loadu_si128((__m128i*)(a)); 
xmm1 = _mm_loadu_si128((__m128i*)(b)); 

此代码已修改为@harold建议。这里我们从给定的内存指针(可能是未对齐的)加载16个字节到变量xmm0xmm1。在汇编代码中,这些变量很可能直接位于寄存器中,因此这种内在函数会产生未对齐的内存负载。将指针转换为__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 
}