Skip to content

Commit 512dda9

Browse files
committed
Optimize slice binary_search_by
1 parent 1b9efcd commit 512dda9

File tree

1 file changed

+12
-24
lines changed

1 file changed

+12
-24
lines changed

library/core/src/slice/mod.rs

+12-24
Original file line numberDiff line numberDiff line change
@@ -2887,36 +2887,24 @@ 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()`:
2895+
// mid = self.len() / 2 + self.len() % 2 +
2896+
// self.len() / 4 + self.len() % 4 +
2897+
// self.len() / 8 + self.len() % 8 ...
29012898
let cmp = f(unsafe { self.get_unchecked(mid) });
29022899

2903-
// Binary search interacts poorly with branch prediction, so force
2904-
// the compiler to use conditional moves if supported by the target
2905-
// 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;
2900+
base = match cmp {
2901+
Less => mid + (size & 1),
2902+
Equal => return Ok(mid),
2903+
Greater => base,
2904+
};
29172905
}
29182906

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

0 commit comments

Comments
 (0)