Change:
This affects my real project. seems to cause a huge performance regression. After investigation, it is caused by the new binarysearch introduced by this pr . Why is the new not ? macOS Pro M2 <details</summary<p</p</details<details</summary<p</p</details
If your use-case permits it you can try partition_point instead which doesn't check the equality case. Its limitation is that it doesn't distinguish the presence/absence of the element at the returned index.
There seems to be a huge performance regression only in aarch64. x86_64 i7-9750H !
I haven't run your benchmark yet, but was merged which might reclaim some performance. Using from your ,
Hi, thank you very much for your contribution. Here are the new benchmarks. (MacBook Pro M2). It looks like is faster than , but still not as good as . <details</summary</details<details</summary</details<details</summary</details
x86_64 i7-9750H. !
See for a potential fix.
(MacBook M3) Just tested it locally, and it is undoubtedly better than all binary_search ever

Discussion:
#![stable(feature = "rust1", since = "1.0.0")] use crate::cmp::Ordering::{self, Equal, Greater, Less}; <del> use crate::intrinsics::{exact_div, unchecked_sub}; </del> <ins> use crate::intrinsics::{exact_div, select_unpredictable, unchecked_sub}; </ins> use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; use crate::ops::{Bound, OneSidedRange, Range, RangeBounds};

where F: FnMut(&'a T) -> Ordering, { <del> // INVARIANTS: // - 0 <= left <= left + size = right <= self.len() // - f returns Less for everything in self[..left] // - f returns Greater for everything in self[right..] </del> let mut size = self.len(); <del> let mut left = 0; let mut right = size; while left < right { let mid = left + size / 2; // SAFETY: the while condition means `size` is strictly positive, so // `size/2 < size`. Thus `left + size/2 < left + size`, which // coupled with the `left + size <= self.len()` invariant means // we have `left + size/2 < self.len()`, and this is in-bounds. </del> <ins> if size == 0 { return Err(0); } let mut base = 0usize; // This loop intentionally doesn't have an early exit if the comparison // returns Equal. We want the number of loop iterations to depend *only* // on the size of the input slice so that the CPU can reliably predict // the loop count. while size > 1 { let half = size / 2; let mid = base + half; // SAFETY: the call is made safe by the following inconstants: // - `mid >= 0`: by definition // - `mid < size`: `mid = size / 2 + size / 4 + size / 8 ...` </ins> let cmp = f(unsafe { self.get_unchecked(mid) }); <del> // This control flow produces conditional moves, which results in // fewer branches and instructions than if/else or matching on // cmp::Ordering. // This is x86 asm for u8: https://rust.godbolt.org/z/698eYffTx. left = if cmp == Less { mid + 1 } else { left }; right = if cmp == Greater { mid } else { right }; if cmp == Equal { // SAFETY: same as the `get_unchecked` above unsafe { hint::assert_unchecked(mid < self.len()) }; return Ok(mid); } size = right - left; </del> <ins> // Binary search interacts poorly with branch prediction, so force // the compiler to use conditional moves if supported by the target // architecture. base = select_unpredictable(cmp == Greater, base, mid); // This is imprecise in the case where `size` is odd and the // comparison returns Greater: the mid element still gets included // by `size` even though it's known to be larger than the element // being searched for. // // This is fine though: we gain more performance by keeping the // loop iteration count invariant (and thus predictable) than we // lose from considering one additional element. size -= half; </ins> } <del> // SAFETY: directly true from the overall invariant. // Note that this is `<=`, unlike the assume in the `Ok` path. unsafe { hint::assert_unchecked(left <= self.len()) }; Err(left) </del> <ins> // SAFETY: base is always in [0, size) because base <= mid. let cmp = f(unsafe { self.get_unchecked(base) }); if cmp == Equal { // SAFETY: same as the `get_unchecked` above. unsafe { hint::assert_unchecked(base < self.len()) }; Ok(base) } else { let result = base + (cmp == Less) as usize; // SAFETY: same as the `get_unchecked` above. // Note that this is `<=`, unlike the assume in the `Ok` path. unsafe { hint::assert_unchecked(result <= self.len()) }; Err(result) } </ins> } /// Binary searches this slice with a key extraction function.

assert_eq!(b.binary_search(&8), Err(5)); let b = [(); usize::MAX]; <del> assert_eq!(b.binary_search(&()), Ok(usize::MAX / 2)); </del> <ins> assert_eq!(b.binary_search(&()), Ok(usize::MAX - 1)); </ins> } #[test] fn test_binary_search_by_overflow() { let b = [(); usize::MAX]; <del> assert_eq!(b.binary_search_by(|_| Ordering::Equal), Ok(usize::MAX / 2)); </del> <ins> assert_eq!(b.binary_search_by(|_| Ordering::Equal), Ok(usize::MAX - 1)); </ins> assert_eq!(b.binary_search_by(|_| Ordering::Greater), Err(0)); assert_eq!(b.binary_search_by(|_| Ordering::Less), Err(usize::MAX)); }

let b = [1, 1, 2, 2, 3, 3, 3]; assert_eq!(b.binary_search(&1), Ok(1)); assert_eq!(b.binary_search(&2), Ok(3)); <del> assert_eq!(b.binary_search(&3), Ok(5)); </del> <ins> assert_eq!(b.binary_search(&3), Ok(6)); </ins> let b = [1, 1, 1, 1, 1, 3, 3, 3, 3]; assert_eq!(b.binary_search(&1), Ok(4)); <del> assert_eq!(b.binary_search(&3), Ok(7)); </del> <ins> assert_eq!(b.binary_search(&3), Ok(8)); </ins> let b = [1, 1, 1, 1, 3, 3, 3, 3, 3]; <del> assert_eq!(b.binary_search(&1), Ok(2)); assert_eq!(b.binary_search(&3), Ok(4)); </del> <ins> assert_eq!(b.binary_search(&1), Ok(3)); assert_eq!(b.binary_search(&3), Ok(8)); </ins> } #[test]