[PATCH] Optimize strstr, strcasestr and memmem

Ondřej Bílka neleai@seznam.cz
Fri May 18 06:18:00 GMT 2012


On Fri, May 18, 2012 at 02:25:47PM +1200, Maxim Kuvyrkov wrote:
> On 18/05/2012, at 10:20 AM, Joseph S. Myers wrote:
> 
> > Anyone interested in performance of these functions may also be interested 
> > in bug 12100, where the SSE4 version of strstr reintroduces the unwanted 
> > quadratic asymptotic performance.  (This is not a comment on your patch 
> > itself, but a mention of something people looking at this area might also 
> > be interested in.)
> 
> Thanks for the pointer.  For avoidance of doubt, I've benchmarked the patch on a Core2 machine without SSE4.
> 
> I will benchmark the SSE4 implementation against the normal + this patch on short needles.  The benchmark that motivated this patch is libosip message parsing, which heavily uses string functions with small strings.
> 

I posted strstr implementation at this list and got no response. 

I use only SSE2 instructions and my algorithm is faster than using SSE4 instructions.
With trivial modifications I could also use plain arithmetic/AVX2

A trick is to check first two characters or zero terminator in parallel.
This is about 25 times faster on core2 vs glibc one, on i7 its only 3
times faster.

I use these trick for my extension of regular expression engine a
partialy autogenerated strstr follows.


> --
> Maxim Kuvyrkov
> CodeSourcery / Mentor Graphics

-- 

transient bus protocol violation

-------------- next part --------------
#ifdef ARITHMETIC
#include "arithmetic.h"
#else
#include "sse2.h"
#endif
char * strstr2(char *s){ 
  c1=n: c1=n+1;
  int i; char *p; MBTYPE e0;MBTYPE e1;MBTYPE e2;MBTYPE e3;MBTYPE e4;MBTYPE e5;MBTYPE e6;MBTYPE e7;
  MBTYPE m0=make_mask(*c1,0);MBTYPE m1=make_mask(*c2,1);MBTYPE m2=make_mask(0,0);
  int time=0;
  int offset=((long)(s))%BYTES_AT_ONCE;
  s-=offset;
  MBTYPE s2=LOAD(s);
  e0=test_eq(s2, m0);
  e1=test_eq(shift_down(s2,1), m1);

  e3=AND(e0,e1);
  e4=test_eq(s2, m2);

  e6=e4;
  e7=OR(e3,e6);
  MASKTYPE mask=get_mask(e7);
  mask=forget_bits(mask,offset);
  while (1){
    if(mask){
      for(i=0;i<BYTES_AT_ONCE;i++) if (GET_BIT(mask,i)){
        if (GET_BIT(get_mask(e3),i)){p=s+i+2; 
          int j;
          for (j=0;n[j+2] && p[j]==n[j+2];j++);
          if (!n[j+2]) return p-2;
          time+=j;
          if (time>16*(s2-s)) return strstr(p-2,n);
        }
        if (GET_BIT(get_mask(e6),i)){p=s+i+1; return NULL;}

      }
    }
    s+=BYTES_AT_ONCE;
    s2=LOAD(s);
    e0=test_eq(s2, m0);
    e1=test_eq(shift_down(s2,1), m1);

    e3=AND(e0,e1);
    e4=test_eq(s2, m2);

    e6=e4;
    e7=OR(e3,e6);
    mask=get_mask(e7);
  }
}
-------------- next part --------------
#include <stdint.h>
#include <emmintrin.h>
#define BYTES_AT_ONCE 16
#define MBTYPE __m128i

#define MASKTYPE int
inline MASKTYPE forget_bits(MASKTYPE m,int b){return (m >>(b))<<(b);}




const uint64_t ones=0x0101010101010101;
MBTYPE make_mask(unsigned char c,int shift){
  return _mm_set_epi64x(ones*((uint64_t)c),(ones >> (8*shift))*((uint64_t)c));
}
#define LOAD(x) _mm_load_si128(( long long int * )x)
#define test_eq  _mm_cmpeq_epi8
#define AND  _mm_and_si128
#define OR   _mm_or_si128
#define shift_down _mm_srli_si128
#define shift_up   _mm_slli_si128
inline int  get_mask(MBTYPE x){ return  _mm_movemask_epi8(x); }
#define GET_BIT(x,y) (x&(1<<(y)))


More information about the Libc-alpha mailing list