[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:19:01 PDT 2026


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

During a binary search, compare_result == 0 is triggered at most once, or 0 times when the key is not found, whereas compare_result < 0 and compare_result > 0 are triggered multiple times across loop iterations. Check < 0 and > 0 before the equality case so the loop does not test for == 0 first on every iteration.

With clang -O2, the loop body changes as follows:

x86-64:
```diff
        movq    %rbp, %rsi
        callq   *(%rsp)
        testl   %eax, %eax
-       je      .LBB0_7
        js      .LBB0_5
+       je      .LBB0_7
        notq    %r13
        addq    %rbx, %r13
        addq    %r14, %rbp
```

riscv64:
```diff
        mv      a0, s3
        mv      a1, s6
        jalr    s2
-       beqz    a0, .LBB0_10
        bltz    a0, .LBB0_5
+       beqz    a0, .LBB0_10
        not     s0, s0
        add     s0, s0, s1
        add     s5, s6, s4
```

The generated code now branches on < 0 first, then checks == 0 to exit, and falls through to the > 0 update.

>From 8eb048aa03e1176f3e0db7665d38d8ef703382f4 Mon Sep 17 00:00:00 2001
From: Kuan-Wei Chiu <visitorckw at gmail.com>
Date: Sat, 10 Oct 2026 17:50:33 +0000
Subject: [PATCH] [libc][stdlib] Optimize bsearch() by reordering comparison
 branches

During a binary search, compare_result == 0 is triggered at most once,
or 0 times when the key is not found, whereas compare_result < 0 and
compare_result > 0 are triggered multiple times across loop iterations.
Check < 0 and > 0 before the equality case so the loop does not test
for == 0 first on every iteration.

With clang -O2, the loop body changes as follows:

x86-64:
        movq    %rbp, %rsi
        callq   *(%rsp)
        testl   %eax, %eax
-       je      .LBB0_7
        js      .LBB0_5
+       je      .LBB0_7
        notq    %r13
        addq    %rbx, %r13
        addq    %r14, %rbp

riscv64:
        mv      a0, s3
        mv      a1, s6
        jalr    s2
-       beqz    a0, .LBB0_10
        bltz    a0, .LBB0_5
+       beqz    a0, .LBB0_10
        not     s0, s0
        add     s0, s0, s1
        add     s5, s6, s4

The generated code now branches on < 0 first, then checks == 0 to exit,
and falls through to the > 0 update.
---
 libc/src/stdlib/bsearch.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/libc/src/stdlib/bsearch.cpp b/libc/src/stdlib/bsearch.cpp
index f0848051173ef..7e76f3112bfc2 100644
--- a/libc/src/stdlib/bsearch.cpp
+++ b/libc/src/stdlib/bsearch.cpp
@@ -26,19 +26,18 @@ LLVM_LIBC_FUNCTION(void *, bsearch,
     const void *elem =
         reinterpret_cast<const uint8_t *>(array) + mid * elem_size;
     int compare_result = compare(key, elem);
-    if (compare_result == 0)
-      return const_cast<void *>(elem);
-
     if (compare_result < 0) {
       // This means that key is less than the element at |mid|.
       // So, in the next iteration, we only compare elements less
       // than mid.
       array_size = mid;
-    } else {
+    } else if (compare_result > 0) {
       // |mid| is strictly less than |array_size|. So, the below
       // decrement in |array_size| will not lead to a wrap around.
       array_size -= (mid + 1);
       array = reinterpret_cast<const uint8_t *>(elem) + elem_size;
+    } else {
+      return const_cast<void *>(elem);
     }
   }
 



More information about the libc-commits mailing list