Slow memcmp for aligned strings on Pentium 3

Jerry Quinn jlquinn@optonline.net
Mon Apr 7 17:30:00 GMT 2003


Kevin Atkinson writes:
 > On Sat, 5 Apr 2003, Jerry Quinn wrote:
 > 
 > > Kevin Atkinson writes:
 > >  > On Fri, 4 Apr 2003, Jerry Quinn wrote:
 > >  > 
 > >  > > I just tried the same benchmark on a Pentium 4 out of curiosity.  Slightly
 > >  > > different results:
 > >  > > 
 > >  > > Memory compare int:
 > >  > >   10000
 > >  > >   130000
 > >  > >   Speed up: 0.076923
 > >  > > Memory compare 15 bytes:
 > >  > >   10000
 > >  > >   370000
 > >  > >   Speed up: 0.027027
 > >  > > Memory compare 16 bytes:
 > >  > >   20000
 > >  > >   330000
 > >  > >   Speed up: 0.060606
 > >  > > Memory compare 64 bytes:
 > >  > >   10000
 > >  > >   1040000
 > >  > >   Speed up: 0.009615
 > >  > > Memory compare 256 bytes:
 > >  > >   20000
 > >  > >   2300000
 > >  > >   Speed up: 0.008696
 > >  > > 
 > >  > > Perhaps this is to be expected since the routine uses shifts.
 > >  > 
 > >  > The shift are only used in the case size is not divisible by 4.  It seams 
 > >  > that on the Pentium 4 cmps is the way to go.  You might also want to 
 > >  > increase the number of loop iterations to get more meaning full results 
 > >  > due the limited precision of clock().
 > > 
 > > Adding iterations didn't change the relative scores significantly.  It
 > > still loses big on P4.  It also loses big on Athlon.  Here are Athlon
 > > results using the later version you posted with 10x iterations:
 > > 
 > > jlquinn@smaug:~/gcc/test$ gcc3.3 -O3 -fomit-frame-pointer -march=athlon cmps.c 
 > > jlquinn@smaug:~/gcc/test$ ./a.out 
 > > Memory compare 15 bytes:
 > >   310000
 > >   5810000
 > >   Speed up: 0.053356
 > > Memory compare 16 bytes:
 > >   300000
 > >   5290000
 > >   Speed up: 0.056711
 > > Memory compare 64 bytes:
 > >   460000
 > >   13770000
 > >   Speed up: 0.033406
 > > Memory compare 256 bytes:
 > >   470000
 > 
 > This is extremely interesting.  Does anyone have any documentation on cmps 
 > behavior on P4 and Athlon?  It could be that the processor is somehow 
 > "caching" the results of cmps.  Maybe it has to do with the fact that the 
 > strings are all 0 except for the end or because the strings do not change.  
 > Or maybe cmps is just extremely fast, but how?  Or it could be that the 
 > loop needs unrolling for better pipeline performance.  I don't have a P4 
 > or Athlon so if someone could play around with my code by testing by 
 > testing some of my theories I would appreciate it.
 > 
 > I just ran the test on a Pentium MMX and i got similar results as I did 
 > for my P3.  So at very least it seams that something similar to my code is 
 > the way to go for Pentiums up to P3.

Unrolling makes things even worse for Athlon - or rather MUCH better
for the builtin function:

jlquinn@smaug:~/gcc/test$ gcc3.3 -O3 -fomit-frame-pointer -funroll-loops -mcpu=athlon cmps.c 
jlquinn@smaug:~/gcc/test$ ./a.out 
Memory compare 15 bytes:
  40000
  5440000
  Speed up: 0.007353
Memory compare 16 bytes:
  60000
  5070000
  Speed up: 0.011834
Memory compare 64 bytes:
  60000
  13660000
  Speed up: 0.004392
Memory compare 256 bytes:
  60000
  39300000
  Speed up: 0.001527

If I assign both arrays to the same set of random bytes, the results
tilt a bit more towards your routine :-)

jlquinn@smaug:~/gcc/test$ gcc3.3 -O3 -fomit-frame-pointer -mcpu=athlon cmps.c 
jlquinn@smaug:~/gcc/test$ ./a.out 
Memory compare 15 bytes:
  460000
  5640000
  Speed up: 0.081560
Memory compare 16 bytes:
  450000
  5390000
  Speed up: 0.083488
Memory compare 64 bytes:
  300000
  5250000
  Speed up: 0.057143
Memory compare 256 bytes:
  310000
  5090000
  Speed up: 0.060904

Making the two arrays randomly generated and different from each other
provides yet more improvement:

jlquinn@smaug:~/gcc/test$ gcc3.3 -O3 -fomit-frame-pointer -mcpu=athlon cmps.c 
jlquinn@smaug:~/gcc/test$ ./a.out 
Memory compare 15 bytes:
  310000
  3660000
  Speed up: 0.084699
Memory compare 16 bytes:
  310000
  3510000
  Speed up: 0.088319
Memory compare 64 bytes:
  460000
  3510000
  Speed up: 0.131054
Memory compare 256 bytes:
  460000
  3500000


Still not even close.  I just scanned the Athlon optimization guide
and there is a comment about being sure to align the inputs for
repeated string ops.  That makes it sound like work was done to make
aligned cmpsb efficient.

I also tried commenting out the final int cleanup so that cmpa2 looks
like:

__attribute__((noinline))
int cmpa2(const unsigned int * x, const unsigned int * y, size_t size)
{
  int i = 0;
  size_t s = size / 4;
  while (i < s && x[i] == y[i]) ++i;
  size -= i * 4;
  if (size == 0) return 0;
  return 1;
/*   unsigned int xx = x[i], yy = y[i]; */
/*   asm("bswap %0" : "+r"(xx)); */
/*   asm("bswap %0" : "+r"(yy)); */
/*   if (size >= 4) { */
/*     return xx < yy ? -1 : 1; */
/*   } else { */
/*     unsigned int dis = 8*(4-size); */
/*     xx >>= dis; */
/*     yy >>= dis; */
/*     return xx - yy; */
/*   } */
}


I wanted to see if the compiler could make the simple integer compare
fast.  This is an (expected) improvement, but doesn't come close to
narrowing the gap:

jlquinn@smaug:~/gcc/test$ gcc3.3 cmps.c -O3 -fomit-frame-pointer -mcpu=athlon  
jlquinn@smaug:~/gcc/test$ ./a.out 
Memory compare 15 bytes:
  300000
  2740000
  Speed up: 0.109489
Memory compare 16 bytes:
  310000
  2520000
  Speed up: 0.123016
Memory compare 64 bytes:
  480000
  2590000
  Speed up: 0.185328
Memory compare 256 bytes:
  460000
  2680000
  Speed up: 0.171642

It seems the conclusion is that rep cmpsb is very fast on Athlon.
Also probably for P4, but I haven't tried the same tests beyond the
first one.

Jerry Quinn



More information about the Gcc mailing list