gcc -c -Q -O3 --help=optimizers | grep funroll-loops
  -funroll-loops                        [disabled]

-funroll-loops is the first branch in the root of evil, don't rely on it, even 
force -O3 isn't as good as it seem often.

On Sunday 10 May 2009 19:11:14 Jay Pipes wrote:
> Have we tried running the benchmarks with and without -funroll-loops?
>
> -j
>
> Stewart Smith wrote:
> > So i broke out mtaylor's patch that we were bumming around with at the UC
> > to replace ptr_compare with a simple memcmp call.
> >
> > At the UC I benched that this patch actually caused a measurable
> > performance regresssion.
> >
> > So what's the difference?
> > (same benchmark and machine as in previous mail)
> >
> > MAX_FIELDS=64 with std::bitset
> >     read/write requests:                 70000  (3374.50 per sec.)
> >     read/write requests:                 70000  (3102.42 per sec.)
> >     read/write requests:                 70000  (3113.47 per sec.)
> >     read/write requests:                 70000  (3401.89 per sec.)
> >     read/write requests:                 70000  (3164.61 per sec.)
> > AVERAGE= 3231.37
> >
> > With ptr_compare replaced with memcmp:
> >     read/write requests:                 70000  (3090.33 per sec.)
> >     read/write requests:                 70000  (3066.97 per sec.)
> >     read/write requests:                 70000  (2954.93 per sec.)
> >     read/write requests:                 70000  (2875.57 per sec.)
> >     read/write requests:                 70000  (2953.99 per sec.)
> > AVERAGE= 2988.35
> >
> > With ptr_compare replaced with __builtin_memcmp:
> >     read/write requests:                 70000  (3245.37 per sec.)
> >     read/write requests:                 70000  (3001.91 per sec.)
> >     read/write requests:                 70000  (3254.99 per sec.)
> >     read/write requests:                 70000  (3177.13 per sec.)
> >     read/write requests:                 70000  (3195.49 per sec.)
> > AVERAGE= 3174.97
> >
> >
> > So, I  thought that with just using the __builtin_memcmp automatically,
> > we may be okay with merging this.
> >
> > However... I do have the following concerns:
> > 1) I'm pretty sure that __builtin_memcmp is gcc only, and that SunStudio
> > may have an issue (yay - wrappers!)
> > 2) Is this perf difference going to be true on different platforms?
> > 3) the various memcmp implementations do seem to be dependent on a few
> > things for performance: alignment, size of data.
> >
> > For what we're seeing in sysbench, the parameters are things like this:
> > "SELECT c from sbtest where id between 9942 and 10041 order by c"
> >
> > 0xc30780,0xc2c340,240
> > 0xc2e560,0xc2c340,240
> > 0xc30780,0xc2e560,240
> > 0xc37390,0xc32f50,240
> > 0xc35170,0xc32f50,240
> > 0xc37390,0xc35170,240
> > 0xc3dcc8,0xc39888,240
> > 0xc3baa8,0xc2e560,240
> > 0xc35170,0xc2e560,240
> > 0xc3baa8,0xc35170,240
> > 0xc2c340,0xc35170,240
> > 0xc35170,0xc3dcc8,240
> > 0xc2c618,0xc35170,240
> > 0xc35170,0xc3d9f0,240
> > 0xc2c8f0,0xc35170,240
> > 0x7fbed85f10f0,0x7fbed85eccb0,240
> >
> > and from the CREATE TABLE:
> >         `c` VARCHAR(120) NOT NULL  COLLATE utf8_general_ci DEFAULT `` ,
> >
> > So here it's large, and aligned.
> >
> > For some other micro-benchmarks I've clocked things looking much
> > different.... so it's possibly query dependent as to what ends up being
> > better (i.e. how much data we're comparing).
> >
> > (1st parameter is number of repetitions, 2nd is number of bytes to
> > memcmp)
> >
> >
> > For comparing equal values:
> >
> > stew...@willster:~/src/test/memcmp$ ./a.out 168435455 8
> > 168435455 repetitions
> >
> > Testing memcmp ..... done,      7.432 seconds
> > Testing builtin memcmp ....... done,     16.313 seconds
> > Testing loop .... done,      6.144 seconds
> > Testing loop32 .... done,      2.764 seconds
> > Testing loop64 .... done,      2.128 seconds
> > Testing no-op .... done,      1.488 seconds
> > stew...@willster:~/src/test/memcmp$ ./a.out 168435455 16
> > 168435455 repetitions
> >
> > Testing memcmp ..... done,      5.912 seconds
> > Testing builtin memcmp ....... done,     26.418 seconds
> > Testing loop .... done,     11.201 seconds
> > Testing loop32 .... done,      3.980 seconds
> > Testing loop64 .... done,      2.564 seconds
> > Testing no-op .... done,      1.492 seconds
> > stew...@willster:~/src/test/memcmp$ ./a.out 168435455 32
> > 168435455 repetitions
> >
> > Testing memcmp ..... done,      6.828 seconds
> > Testing builtin memcmp ....... done,     46.891 seconds
> > Testing loop .... done,     21.473 seconds
> > Testing loop32 .... done,      6.536 seconds
> > Testing loop64 .... done,      3.804 seconds
> > Testing no-op .... done,      1.476 seconds
> > stew...@willster:~/src/test/memcmp$ ./a.out 168435455 64
> > 168435455 repetitions
> >
> > Testing memcmp ..... done,      9.549 seconds
> > Testing builtin memcmp ....... done,     87.513 seconds
> > Testing loop .... done,     41.455 seconds
> > Testing loop32 .... done,     11.669 seconds
> > Testing loop64 .... done,      6.368 seconds
> > Testing no-op .... done,      1.468 seconds
> > stew...@willster:~/src/test/memcmp$ ./a.out 168435455 128
> > 168435455 repetitions
> >
> > Testing memcmp ..... done,     15.081 seconds
> > Testing builtin memcmp ....... done,    169.143 seconds
> > Testing loop .... done,     86.397 seconds
> > Testing loop32 .... done,     21.877 seconds
> > Testing loop64 .... done,     11.445 seconds
> > Testing no-op .... done,      1.488 seconds
> > stew...@willster:~/src/test/memcmp$ gcc -O3 -fno-builtin memcmpbench.c
> > stew...@willster:~/src/test/memcmp$ ./a.out 168435455 256
> > 168435455 repetitions
> >
> > Testing memcmp ..... done,     26.134 seconds
> > Testing loop64 .... done,     21.549 seconds
> > Testing no-op .... done,      1.500 seconds
> >
> >
> >
> > Completely inequal values are all about the same:
> >
> > stew...@willster:~/src/test/memcmp$ ./a.out 168435455 8
> > 168435455 repetitions
> >
> > Testing memcmp ..... done,      3.204 seconds
> > Testing builtin memcmp ....... done,     13.945 seconds
> > Testing loop .... done,      1.896 seconds
> > Testing loop32 .... done,      2.092 seconds
> > Testing loop64 .... done,      2.136 seconds
> > Testing no-op .... done,      1.488 seconds
> >
> > stew...@willster:~/src/test/memcmp$ ./a.out 168435455 240
> > 168435455 repetitions
> >
> > Testing memcmp ..... done,      5.520 seconds
> > Testing builtin memcmp ....... done,     13.973 seconds
> > Testing loop .... done,      1.912 seconds
> > Testing loop32 .... done,      2.124 seconds
> > Testing loop64 .... done,      2.104 seconds
> > Testing no-op .... done,      1.468 seconds
> >
> >
> > For half the same:
> > stew...@willster:~/src/test/memcmp$ ./a.out 168435455 8
> > 168435455 repetitions
> >
> > Testing memcmp ..... done,      7.340 seconds
> > Testing builtin memcmp ....... done,     19.193 seconds
> > Testing loop .... done,      4.212 seconds
> > Testing loop32 .... done,      2.956 seconds
> > Testing loop64 .... done,      2.112 seconds
> > Testing no-op .... done,      1.476 seconds
> >
> > stew...@willster:~/src/test/memcmp$ ./a.out 168435455 32
> > 168435455 repetitions
> >
> > Testing memcmp ..... done,      5.920 seconds
> > Testing builtin memcmp ....... done,     34.526 seconds
> > Testing loop .... done,     11.885 seconds
> > Testing loop32 .... done,      4.444 seconds
> > Testing loop64 .... done,      3.388 seconds
> > Testing no-op .... done,      1.468 seconds
> >
> > stew...@willster:~/src/test/memcmp$ ./a.out 168435455 64
> > 168435455 repetitions
> >
> > Testing memcmp ..... done,      6.948 seconds
> > Testing builtin memcmp ....... done,     54.923 seconds
> > Testing loop .... done,     22.081 seconds
> > Testing loop32 .... done,      7.000 seconds
> > Testing loop64 .... done,      4.444 seconds
> > Testing no-op .... done,      1.488 seconds
> >
> >
> > Is concurrency an issue here and not raw single threaded performance?
> >
> > Let's try with 64 threads (on the same 2 core box):
> >
> > 64 threads, __builtin_memcmp:
> >     read/write requests:                 70000  (3184.20 per sec.)
> >     read/write requests:                 70000  (3184.87 per sec.)
> >     read/write requests:                 70000  (3166.83 per sec.)
> >     read/write requests:                 70000  (3085.69 per sec.)
> > AVERAGE=3155.39
> >
> > 64 threads, memcmp:
> >     read/write requests:                 70000  (3218.07 per sec.)
> >     read/write requests:                 70000  (3219.04 per sec.)
> >     read/write requests:                 70000  (3222.48 per sec.)
> >     read/write requests:                 70000  (3116.00 per sec.)
> > AVERAGE=3193.89
> >
> >
> > 64 threads, 32bit cmp loop:
> >     read/write requests:                 70000  (3173.76 per sec.)
> >     read/write requests:                 70000  (3156.23 per sec.)
> >     read/write requests:                 70000  (3206.70 per sec.)
> >     read/write requests:                 70000  (3247.61 per sec.)
> > AVERAGE=3196.07
> >
> > 64 threads, 64bit cmp loop:
> >     read/write requests:                 70000  (3210.02 per sec.)
> >     read/write requests:                 70000  (3218.87 per sec.)
> >     read/write requests:                 70000  (3225.98 per sec.)
> >     read/write requests:                 70000  (3131.60 per sec.)
> > AVERAGE= 3196.61
> >
> > 64 threads, baseline (using original ptr_compare):
> >     read/write requests:                 70000  (3542.95 per sec.)
> >     read/write requests:                 70000  (3556.15 per sec.)
> >     read/write requests:                 70000  (3560.71 per sec.)
> >     read/write requests:                 70000  (3476.80 per sec.)
> > AVERAGE=3534.15
> >
> > i.e. the old ptr_compare whoops the arse of any of the memcmp calls with
> > higher concurrency.
> >
> >
> > So, after this I can conclude:
> > - memcmp is nothing if not consistent across various microbenchmarks
> > - builtin_memcmp seems to help a bit at lower concurrency, not at all at
> > higher and is seldom useful in micro benchmark.
> >
> > - the 64bit loop isn't so good at higher concurrency
> > - the 64bit loop is favourable in microbenchmarks
> > - memcmp is close to the 64bit loop performance (at most only 2x slower)
> >
> > Unrolling loops being the key?
> >
> > Testing loop .... done,     11.813 seconds
> > Testing unrollloop .... done,      7.628 seconds
> > Testing unrollloop32 .... done,      2.532 seconds
> > Testing unrollloop64 .... done,      2.536 seconds
> > Testing loop32 .... done,      4.432 seconds
> > Testing loop64 .... done,      3.792 seconds
> > Testing no-op .... done,      1.276 seconds
> >
> > (32 and 64 do 32,64 bit compares in an unrolled loop)
> >
> > Questions:
> > - Why does my 64bit compare loop not equal performance of ptr_compare
> > unrolled loop? (in a microbenchmark, it beats it). Concurrency again?
> >
> >
> > So what should we do?
> >
> > Certainly a  call to memcmp is easier to understand and keeps the code
> > nice and simple, but we're talking up to a 10% speed hit here at higher
> > concurrency (at least on my hardware).
> >
> > I wonder what the difference is on various other hardware setups.
> >
> > I'd be very interested to see what happens on sparc.
> >
> > I'd also like to see it micro-benchmarked - preferably something we
> > could repeat in future (even in ./configure ? on server startup ?)
> >
> > I'm voting to keep the current ptr_compare code, and hope that somebody
> > provides further explanation on top of what I've looked at here.
> >
> >
> >
> >
> > Below is the patch, followed by the code i used for microbenchmarking
> > (note the commented out 64bit loop i used for the above loop tests):
> >
> > === modified file 'drizzled/filesort.cc'
> > --- drizzled/filesort.cc    2009-04-28 00:17:10 +0000
> > +++ drizzled/filesort.cc    2009-05-08 05:59:11 +0000
> > @@ -1135,7 +1135,7 @@ int merge_buffers(SORTPARAM *param, IO_C
> >    }
> >    else
> >    {
> > -    cmp= get_ptr_compare(sort_length);
> > +    cmp= (qsort2_cmp)ptr_compare;
> >      first_cmp_arg= (void*) &sort_length;
> >    }
> >    priority_queue<BUFFPEK *, vector<BUFFPEK *>, compare_functor >
> >
> > === modified file 'mysys/mf_sort.cc'
> > --- mysys/mf_sort.cc        2009-04-17 21:01:47 +0000
> > +++ mysys/mf_sort.cc        2009-05-08 05:59:11 +0000
> > @@ -34,7 +34,7 @@ void my_string_ptr_sort(unsigned char *b
> >    {
> >      if (size && items)
> >      {
> > -      my_qsort2(base,items, sizeof(unsigned char*),
> > get_ptr_compare(size), +      my_qsort2(base,items, sizeof(unsigned
> > char*), (qsort2_cmp)ptr_compare, (void*) &size);
> >      }
> >    }
> >
> > === modified file 'mysys/my_sys.h'
> > --- mysys/my_sys.h  2009-04-27 22:05:43 +0000
> > +++ mysys/my_sys.h  2009-05-08 05:59:11 +0000
> > @@ -420,7 +420,12 @@ extern void my_qsort(void *base_ptr, siz
> >                       qsort_cmp cmp);
> >  extern void my_qsort2(void *base_ptr, size_t total_elems, size_t size,
> >                        qsort2_cmp cmp, void *cmp_argument);
> > -extern qsort2_cmp get_ptr_compare(size_t);
> > +
> > +#if defined(__cplusplus)
> > +extern "C"
> > +#endif
> > +int ptr_compare(size_t *compare_length, unsigned char **a, unsigned char
> > **b); +
> >  void my_store_ptr(unsigned char *buff, size_t pack_length, my_off_t
> > pos); my_off_t my_get_ptr(unsigned char *ptr, size_t pack_length);
> >  File create_temp_file(char *to, const char *dir, const char *pfx,
> >
> > === modified file 'mysys/ptr_cmp.cc'
> > --- mysys/ptr_cmp.cc        2009-04-26 16:53:32 +0000
> > +++ mysys/ptr_cmp.cc        2009-05-08 05:59:11 +0000
> > @@ -21,137 +21,33 @@
> >
> >  #include "mysys/mysys_priv.h"
> >  #include "plugin/myisam/myisampack.h"
> > -
> > -static int ptr_compare(size_t *compare_length, unsigned char **a,
> > unsigned char **b); -static int ptr_compare_0(size_t *compare_length,
> > unsigned char **a, unsigned char **b); -static int ptr_compare_1(size_t
> > *compare_length, unsigned char **a, unsigned char **b); -static int
> > ptr_compare_2(size_t *compare_length, unsigned char **a, unsigned char
> > **b); -static int ptr_compare_3(size_t *compare_length, unsigned char
> > **a, unsigned char **b); -
> > -   /* Get a pointer to a optimal byte-compare function for a given size */
> > -
> > -qsort2_cmp get_ptr_compare (size_t size)
> > -{
> > -  if (size < 4)
> > -    return (qsort2_cmp) ptr_compare;
> > -  switch (size & 3) {
> > -    case 0: return (qsort2_cmp) ptr_compare_0;
> > -    case 1: return (qsort2_cmp) ptr_compare_1;
> > -    case 2: return (qsort2_cmp) ptr_compare_2;
> > -    case 3: return (qsort2_cmp) ptr_compare_3;
> > -    }
> > -  return 0;                                        /* Impossible */
> > -}
> > -
> > +#include <string.h>
> >
> >     /*
> >       Compare to keys to see witch is smaller.
> > -     Loop unrolled to make it quick !!
> >     */
> >
> > -#define cmp(N) if (first[N] != last[N]) return (int) first[N] - (int)
> > last[N] -
> > -static int ptr_compare(size_t *compare_length, unsigned char **a,
> > unsigned char **b) -{
> > -  register int length= *compare_length;
> > -  register unsigned char *first,*last;
> > -
> > -  first= *a; last= *b;
> > -  while (--length)
> > -  {
> > -    if (*first++ != *last++)
> > -      return (int) first[-1] - (int) last[-1];
> > -  }
> > -  return (int) first[0] - (int) last[0];
> > -}
> > -
> > -
> > -static int ptr_compare_0(size_t *compare_length,unsigned char **a,
> > unsigned char **b) +extern "C"
> > +int ptr_compare(size_t *compare_length, unsigned char **a, unsigned char
> > **b) {
> > -  register int length= *compare_length;
> > -  register unsigned char *first,*last;
> > -
> > -  first= *a; last= *b;
> > - loop:
> > -  cmp(0);
> > -  cmp(1);
> > -  cmp(2);
> > -  cmp(3);
> > -  if ((length-=4))
> > +/*  size_t l= *compare_length / sizeof(uint64_t);
> > +  uint64_t *aa= (uint64_t*)*a;
> > +  uint64_t *bb= (uint64_t*)*b;
> > +  while(l--)
> >    {
> > -    first+=4;
> > -    last+=4;
> > -    goto loop;
> > -  }
> > -  return (0);
> > -}
> > -
> > -
> > -static int ptr_compare_1(size_t *compare_length,unsigned char **a,
> > unsigned char **b) -{
> > -  register int length= *compare_length-1;
> > -  register unsigned char *first,*last;
> > -
> > -  first= *a+1; last= *b+1;
> > -  cmp(-1);
> > - loop:
> > -  cmp(0);
> > -  cmp(1);
> > -  cmp(2);
> > -  cmp(3);
> > -  if ((length-=4))
> > -  {
> > -    first+=4;
> > -    last+=4;
> > -    goto loop;
> > -  }
> > -  return (0);
> > -}
> > -
> > -static int ptr_compare_2(size_t *compare_length,unsigned char **a,
> > unsigned char **b) -{
> > -  register int length= *compare_length-2;
> > -  register unsigned char *first,*last;
> > -
> > -  first= *a +2 ; last= *b +2;
> > -  cmp(-2);
> > -  cmp(-1);
> > - loop:
> > -  cmp(0);
> > -  cmp(1);
> > -  cmp(2);
> > -  cmp(3);
> > -  if ((length-=4))
> > -  {
> > -    first+=4;
> > -    last+=4;
> > -    goto loop;
> > +    if(*aa != *bb)
> > +    {
> > +      if(*aa < *bb)
> > +   return -1;
> > +      else
> > +   return 1;
> > +    }
> > +    aa++, bb++;
> >    }
> > -  return (0);
> > +  return 0;
> > +*/  return memcmp(*a, *b, *compare_length);
> >  }
> >
> > -static int ptr_compare_3(size_t *compare_length,unsigned char **a,
> > unsigned char **b) -{
> > -  register int length= *compare_length-3;
> > -  register unsigned char *first,*last;
> > -
> > -  first= *a +3 ; last= *b +3;
> > -  cmp(-3);
> > -  cmp(-2);
> > -  cmp(-1);
> > - loop:
> > -  cmp(0);
> > -  cmp(1);
> > -  cmp(2);
> > -  cmp(3);
> > -  if ((length-=4))
> > -  {
> > -    first+=4;
> > -    last+=4;
> > -    goto loop;
> > -  }
> > -  return (0);
> > -}
> >
> >  void my_store_ptr(unsigned char *buff, size_t pack_length, my_off_t pos)
> >  {
> >
> >
> > For those interested, i started with the code at
> > http://gcc.gnu.org/ml/gcc/2002-10/msg01666.html
> >
> > and ended up with something like this:
> >
> > #include <sys/resource.h>
> > #include <sys/time.h>
> > #include <stdio.h>
> > #include <assert.h>
> > #include <stdlib.h>
> >
> > char s1[256];
> > char s2[256];
> >
> > int cmpsize= 240;
> >
> > int
> > speed_memcmp ()
> > {
> >   return memcmp (s1, s2, cmpsize);
> > }
> >
> > int
> > speed_bimemcmp ()
> > {
> >   return __builtin_memcmp (s1, s2, cmpsize);
> > }
> >
> > int
> > speed_loop ()
> > {
> >   int i=cmpsize;
> >   char *a= s1;
> >   char *b= s2;
> >   while(i--)
> >   {
> >     if(*a != *b)
> >       return -1;
> >     a++, b++;
> >   };
> >   return 0;
> > }
> >
> > int
> > speed_loop32 ()
> > {
> >   int i=cmpsize/4;
> >   int *a= (int*)s1;
> >   int *b= (int*)s2;
> >   while(i--)
> >   {
> >     if(*a != *b)
> >       return -1;
> >     a++, b++;
> >   };
> >   return 0;
> > }
> >
> > int
> > speed_loop64 ()
> > {
> >   int i=cmpsize/8;
> >   long long *a= (long long*)s1;
> >   long long *b= (long long*)s2;
> >   while(i--)
> >   {
> >     if(*a != *b)
> >       return -1;
> >     a++, b++;
> >   };
> >
> >   return 0;
> > }
> >
> > int speed_null()
> > {
> >   return 0;
> > }
> >
> >
> > double
> > do_test (int repetitions, int (*test_function) ())
> > {
> >   struct rusage r1, r2;
> >
> >   getrusage(RUSAGE_SELF, &r1);
> >
> >   while (repetitions--)
> >     test_function ();
> >
> >   getrusage(RUSAGE_SELF, &r2);
> >   return (r2.ru_utime.tv_sec - r1.ru_utime.tv_sec) +
> >      (r2.ru_utime.tv_usec - r1.ru_utime.tv_usec) / 1000000.0;
> > }
> >
> > main(int argc, char **argv)
> > {
> >   int repetitions = (argc == 1) ? 0x0fffffff : atoi(argv[1]);
> >   cmpsize = (argc == 2) ? 240 : atoi(argv[2]);
> >
> >   memset(s1, 42, 256);
> >   memset(s2, 42, 256);
> >   memset(s2+(cmpsize/2), 55, cmpsize/2);
> >
> >   printf ("%d repetitions\n\n", repetitions);
> >   printf ("Testing memcmp .....");
> >   fflush (stdout);
> >   printf (" done, %10.3f seconds\n", do_test (repetitions,
> > speed_memcmp));
> >
> >   printf ("Testing builtin memcmp .......");
> >   fflush (stdout);
> >   printf (" done, %10.3f seconds\n", do_test (repetitions,
> > speed_bimemcmp));
> >
> >   printf ("Testing loop ....");
> >   fflush (stdout);
> >   printf (" done, %10.3f seconds\n", do_test (repetitions, speed_loop));
> >
> >   printf ("Testing loop32 ....");
> >   fflush (stdout);
> >   printf (" done, %10.3f seconds\n", do_test (repetitions,
> > speed_loop32));
> >
> >   printf ("Testing loop64 ....");
> >   fflush (stdout);
> >   printf (" done, %10.3f seconds\n", do_test (repetitions,
> > speed_loop64));
> >
> >   printf ("Testing no-op ....");
> >   fflush (stdout);
> >   printf (" done, %10.3f seconds\n", do_test (repetitions, speed_null));
> >
> >   exit (0);
> > }
>
> _______________________________________________
> Mailing list: https://launchpad.net/~drizzle-discuss
> Post to     : [email protected]
> Unsubscribe : https://launchpad.net/~drizzle-discuss
> More help   : https://help.launchpad.net/ListHelp


_______________________________________________
Mailing list: https://launchpad.net/~drizzle-discuss
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~drizzle-discuss
More help   : https://help.launchpad.net/ListHelp

Reply via email to