Refactor binary_search_by to use conditional moves

Refactor the if/else checking on cmp::Ordering variants to a
"branchless" reassignment of left and right. This change results
in fewer branches and instructions.
This commit is contained in:
okaneco 2023-11-08 14:44:13 -05:00
parent 341efb1017
commit d585eecb05

View File

@ -6,7 +6,7 @@
#![stable(feature = "rust1", since = "1.0.0")] #![stable(feature = "rust1", since = "1.0.0")]
use crate::cmp::Ordering::{self, Greater, Less}; use crate::cmp::Ordering::{self, Equal, Greater, Less};
use crate::fmt; use crate::fmt;
use crate::intrinsics::{assert_unsafe_precondition, exact_div}; use crate::intrinsics::{assert_unsafe_precondition, exact_div};
use crate::marker::Copy; use crate::marker::Copy;
@ -2844,14 +2844,13 @@ impl<T> [T] {
// we have `left + size/2 < self.len()`, and this is in-bounds. // we have `left + size/2 < self.len()`, and this is in-bounds.
let cmp = f(unsafe { self.get_unchecked(mid) }); let cmp = f(unsafe { self.get_unchecked(mid) });
// The reason why we use if/else control flow rather than match // This control flow produces conditional moves, which results in
// is because match reorders comparison operations, which is perf sensitive. // fewer branches and instructions than if/else or matching on
// This is x86 asm for u8: https://rust.godbolt.org/z/8Y8Pra. // cmp::Ordering.
if cmp == Less { // This is x86 asm for u8: https://rust.godbolt.org/z/698eYffTx.
left = mid + 1; left = if cmp == Less { mid + 1 } else { left };
} else if cmp == Greater { right = if cmp == Greater { mid } else { right };
right = mid; if cmp == Equal {
} else {
// SAFETY: same as the `get_unchecked` above // SAFETY: same as the `get_unchecked` above
unsafe { crate::intrinsics::assume(mid < self.len()) }; unsafe { crate::intrinsics::assume(mid < self.len()) };
return Ok(mid); return Ok(mid);