Skip to content

Commit b005d21

Browse files
committed
Optimize: core slice binary_search_by
1 parent 16d2276 commit b005d21

File tree

1 file changed

+16
-21
lines changed

1 file changed

+16
-21
lines changed

library/core/src/slice/mod.rs

+16-21
Original file line numberDiff line numberDiff line change
@@ -2887,36 +2887,31 @@ impl<T> [T] {
28872887
}
28882888
let mut base = 0usize;
28892889

2890-
// This loop intentionally doesn't have an early exit if the comparison
2891-
// returns Equal. We want the number of loop iterations to depend *only*
2892-
// on the size of the input slice so that the CPU can reliably predict
2893-
// the loop count.
28942890
while size > 1 {
2895-
let half = size / 2;
2896-
let mid = base + half;
2891+
size >>= 1;
2892+
let mid = base + size;
28972893

2898-
// SAFETY: the call is made safe by the following invariants:
2899-
// - `mid >= 0`: by definition
2900-
// - `mid < size`: `mid = size / 2 + size / 4 + size / 8 ...`
2894+
// SAFETY: `mid < self.len()` because
2895+
// mid = self.len() / 2 + self.len() / 4 + self.len() / 8 ...
29012896
let cmp = f(unsafe { self.get_unchecked(mid) });
29022897

2898+
if cmp == Equal {
2899+
// SAFETY: same as the `get_unchecked` above.
2900+
unsafe { hint::assert_unchecked(mid < self.len()) };
2901+
return Ok(mid);
2902+
}
29032903
// Binary search interacts poorly with branch prediction, so force
29042904
// the compiler to use conditional moves if supported by the target
29052905
// architecture.
2906-
base = hint::select_unpredictable(cmp == Greater, base, mid);
2907-
2908-
// This is imprecise in the case where `size` is odd and the
2909-
// comparison returns Greater: the mid element still gets included
2910-
// by `size` even though it's known to be larger than the element
2911-
// being searched for.
2912-
//
2913-
// This is fine though: we gain more performance by keeping the
2914-
// loop iteration count invariant (and thus predictable) than we
2915-
// lose from considering one additional element.
2916-
size -= half;
2906+
base = hint::select_unpredictable(cmp == Less, mid + (size & 1), base);
2907+
}
2908+
2909+
// if was going through `Ordering::Less` each loop (base = mid + 1)
2910+
if base == self.len() {
2911+
return Err(base);
29172912
}
29182913

2919-
// SAFETY: base is always in [0, size) because base <= mid.
2914+
// SAFETY: `base < self.len()` because `base <= mid`.
29202915
let cmp = f(unsafe { self.get_unchecked(base) });
29212916
if cmp == Equal {
29222917
// SAFETY: same as the `get_unchecked` above.

0 commit comments

Comments
 (0)