[libc-commits] [libc] [libc][stdlib] Optimize bsearch() by reordering comparison branches (PR #230823)

Kuan-Wei Chiu via libc-commits libc-commits at lists.llvm.org
Sat Oct 10 11:25:38 PDT 2026


visitorckw wrote:

For local benchmarking on x86-64, I used the following test program:
```c++
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <stdint.h>

#define N 1024
#define REPS 100000

int arr[N];
int seq_keys[N];
int rand_keys[N];
int miss_keys[N];

__attribute__((noinline, aligned(64))) void *
my_bsearch(const void *key, const void *array, size_t array_size,
           size_t elem_size, int (*compare)(const void *, const void *)) {
  if (key == nullptr || array == nullptr || array_size == 0 || elem_size == 0)
    return nullptr;

  while (array_size > 0) {
    size_t mid = array_size / 2;
    const void *elem =
        reinterpret_cast<const uint8_t *>(array) + mid * elem_size;
    int compare_result = compare(key, elem);
#if AFTER
    if (compare_result < 0) {
      array_size = mid;
    } else if (compare_result > 0) {
      array_size -= (mid + 1);
      array = reinterpret_cast<const uint8_t *>(elem) + elem_size;
    } else {
      return const_cast<void *>(elem);
    }
#else
    if (compare_result == 0)
      return const_cast<void *>(elem);

    if (compare_result < 0) {
      array_size = mid;
    } else {
      array_size -= (mid + 1);
      array = reinterpret_cast<const uint8_t *>(elem) + elem_size;
    }
#endif
  }
  return nullptr;
}

__attribute__((noinline, aligned(64))) int cmp(const void *a, const void *b) {
  int x = *(const int *)a;
  int y = *(const int *)b;
  return (x > y) - (x < y);
}

double bench(const int *keys, int reps) {
  auto start = std::chrono::steady_clock::now();
  for (int r = 0; r < reps; r++) {
    for (int i = 0; i < N; i++) {
      void *p = my_bsearch(&keys[i], arr, N, sizeof(int), cmp);
      asm volatile("" : : "r"(p) : "memory");
    }
  }
  auto end = std::chrono::steady_clock::now();
  return std::chrono::duration<double>(end - start).count();
}

int main() {
  srand(0);
  for (int i = 0; i < N; i++) {
    arr[i] = i * 2;
    seq_keys[i] = i * 2;
    miss_keys[i] = i * 2 + 1;
  }
  for (int i = 0; i < N; i++)
    rand_keys[i] = arr[rand() % N];

  bench(seq_keys, 1000);
  printf("sequential: %.3f s\n", bench(seq_keys, REPS));

  bench(rand_keys, 1000);
  printf("random:     %.3f s\n", bench(rand_keys, REPS));

  bench(miss_keys, 1000);
  printf("miss:       %.3f s\n", bench(miss_keys, REPS));
  return 0;
}
```
Result:
- Before:
```
sequential: 2.518 s
random:     2.738 s
miss:       2.822 s
```
- After:
```
sequential: 2.394 s
random:     2.628 s
miss:       2.641 s
```

https://github.com/llvm/llvm-project/pull/230823


More information about the libc-commits mailing list