[RFC PATCH] Optimize bsearch() implementation for performance

Noah Goldstein goldstein.w.n@gmail.com
Sun Sep 1 23:42:17 GMT 2024


On Sat, Aug 31, 2024 at 6:57 PM Kuan-Wei Chiu <visitorckw@gmail.com> wrote:
>
> On Sun, Sep 01, 2024 at 09:54:56AM +0800, Kuan-Wei Chiu wrote:
> > Optimize the bsearch() function to improve binary search performance.
> > This modification reduces the execution time by approximately 8% on an
> > x86 machine with a 100,000-element array under 1e8 searches. The
> > optimization slightly increases the code size by 8 bytes.
> >
> > code size:
> > * old:
> >    text    data     bss     dec     hex filename
> >     250       0       0     250      fa ./stdlib/bsearch.o
> > * new:
> >    text    data     bss     dec     hex filename
> >     258       0       0     258     102 ./stdlib/bsearch.o
> >
> > benchmark:
> > Old bsearch elapsed time: 4536833
> > New bsearch elapsed time: 4137556
> > Improve efficiency by 8 %
> >
> > Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
> > ---
> > Since I only have x86 machines available for testing, I am not entirely
> > certain whether this patch will bring performance improvements across
> > different architectures. Therefore, I am sending this as an RFC patch
> > first.
> >
> >  bits/stdlib-bsearch.h | 20 +++++++++-----------
> >  1 file changed, 9 insertions(+), 11 deletions(-)
> >
> FWIW, here is the code I used for testing:
>
> /* tst-bsearch1.c */
You could add this as a benchtest in this series :)

If you are interested in doing see other benchmark files
`benchmarks/bench-*.c` for example on how we doing
timing/output (TIMING_* macros and json respectively).

>
> #include <stdio.h>
> #include <stdlib.h>
> #include <time.h>
>
> #define TEST_OLD
>
> int arr[100000];
> #define narr (sizeof (arr) / sizeof (arr[0]))
>
> static int
> comp (const void *p1, const void *p2)
> {
>   int x1 = *(int *) p1;
>   int x2 = *(int *) p2;
>
>   if (x1 < x2)
>     return -1;
>   if (x1 > x2)
>     return 1;
>   return 0;
> }
>
> __attribute__((noinline)) void *
> newbsearch (const void *__key, const void *__base, size_t __nmemb, size_t __size,
>          __compar_fn_t __compar)
> {
>   const void *__p;
>   int __comparison;
>
>   while (__nmemb)
>     {
>       __p = (const void *) (((const char *) __base) + ((__nmemb >> 1) * __size));
>       __comparison = (*__compar) (__key, __p);
>       if (__comparison == 0)
>         {
> #if __GNUC_PREREQ(4, 6)
> # pragma GCC diagnostic push
> # pragma GCC diagnostic ignored "-Wcast-qual"
> #endif
>           return (void *) __p;
> #if __GNUC_PREREQ(4, 6)
> # pragma GCC diagnostic pop
> #endif
>         }
>       if (__comparison > 0)
>         {
>           __base = ((const char *) __p) + __size;
>           --__nmemb;
>         }
>       __nmemb >>= 1;
>     }
>
>   return NULL;
> }
>
> __attribute__((noinline)) void *
> oldbsearch (const void *__key, const void *__base, size_t __nmemb, size_t __size,
>          __compar_fn_t __compar)
> {
>   size_t __l, __u, __idx;
>   const void *__p;
>   int __comparison;
>
>   __l = 0;
>   __u = __nmemb;
>   while (__l < __u)
>     {
>       __idx = (__l + __u) / 2;
>       __p = (const void *) (((const char *) __base) + (__idx * __size));
>       __comparison = (*__compar) (__key, __p);
>       if (__comparison < 0)
>         __u = __idx;
>       else if (__comparison > 0)
>         __l = __idx + 1;
>       else
>         {
> #if __GNUC_PREREQ(4, 6)
> # pragma GCC diagnostic push
> # pragma GCC diagnostic ignored "-Wcast-qual"
> #endif
>           return (void *) __p;
> #if __GNUC_PREREQ(4, 6)
> # pragma GCC diagnostic pop
> #endif
>         }
>     }
>
>   return NULL;
> }
>
> static int
> do_test (void)
> {
>   volatile size_t i;
>   volatile int *res;
>   volatile clock_t begin, end;
>   int key;
>   long long int old_time, new_time;
>
>   for (i = 0; i < narr; ++i)
>     arr[i] = i;
>
>   asm volatile ("" : : : "memory");
>   begin = clock ();
>   asm volatile ("" : : : "memory");
>   for (i = 0; i < 1e8; ++i)
>     {
>       key = i % narr;
>       res = (int *) oldbsearch (&key, arr, narr, sizeof (arr[0]), comp);
>       if (res != arr + key)
>         {
>           printf ("entry %zd got wrong answer\n", i);
>           return 1;
>         }
>     }
>   asm volatile ("" : : : "memory");
>   end = clock ();
>   asm volatile ("" : : : "memory");
>   old_time = end - begin;
>   printf ("Old bsearch elapsed time: %lld\n", old_time);
>
>   asm volatile ("" : : : "memory");
>   begin = clock ();
>   asm volatile ("" : : : "memory");
>   for (i = 0; i < 1e8; ++i)
>     {
>       key = i % narr;
>       res = (int *) newbsearch (&key, arr, narr, sizeof (arr[0]), comp);
>       if (res != arr + key)
>         {
>           printf ("entry %zd got wrong answer\n", i);
>           return 1;
>         }
>     }
>   asm volatile ("" : : : "memory");
>   end = clock ();
>   asm volatile ("" : : : "memory");
>   new_time = end - begin;
>   printf ("New bsearch elapsed time: %lld\n", new_time);
>
>   printf ("Improve efficiency by %lld %%\n", (old_time - new_time) * 100 / old_time);
>
>   return 0;
> }
>
> #define TEST_FUNCTION do_test ()
> #include "../test-skeleton.c"
>


More information about the Libc-alpha mailing list