mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Rollup merge of #133964 - joboet:select_unpredictable, r=tgross35
core: implement `bool::select_unpredictable` Tracking issue: #133962 ACP: https://github.com/rust-lang/libs-team/issues/468
This commit is contained in:
@@ -61,4 +61,52 @@ pub fn then_some<T>(self, t: T) -> Option<T> {
|
||||
pub fn then<T, F: FnOnce() -> T>(self, f: F) -> Option<T> {
|
||||
if self { Some(f()) } else { None }
|
||||
}
|
||||
|
||||
/// Returns either `true_val` or `false_val` depending on the value of
|
||||
/// `self`, with a hint to the compiler that `self` is unlikely
|
||||
/// to be correctly predicted by a CPU’s branch predictor.
|
||||
///
|
||||
/// This method is functionally equivalent to
|
||||
/// ```ignore (this is just for illustrative purposes)
|
||||
/// fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T {
|
||||
/// if b { true_val } else { false_val }
|
||||
/// }
|
||||
/// ```
|
||||
/// but might generate different assembly. In particular, on platforms with
|
||||
/// a conditional move or select instruction (like `cmov` on x86 or `csel`
|
||||
/// on ARM) the optimizer might use these instructions to avoid branches,
|
||||
/// which can benefit performance if the branch predictor is struggling
|
||||
/// with predicting `condition`, such as in an implementation of binary
|
||||
/// search.
|
||||
///
|
||||
/// Note however that this lowering is not guaranteed (on any platform) and
|
||||
/// should not be relied upon when trying to write constant-time code. Also
|
||||
/// be aware that this lowering might *decrease* performance if `condition`
|
||||
/// is well-predictable. It is advisable to perform benchmarks to tell if
|
||||
/// this function is useful.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Distribute values evenly between two buckets:
|
||||
/// ```
|
||||
/// #![feature(select_unpredictable)]
|
||||
///
|
||||
/// use std::hash::BuildHasher;
|
||||
///
|
||||
/// fn append<H: BuildHasher>(hasher: &H, v: i32, bucket_one: &mut Vec<i32>, bucket_two: &mut Vec<i32>) {
|
||||
/// let hash = hasher.hash_one(&v);
|
||||
/// let bucket = (hash % 2 == 0).select_unpredictable(bucket_one, bucket_two);
|
||||
/// bucket.push(v);
|
||||
/// }
|
||||
/// # let hasher = std::collections::hash_map::RandomState::new();
|
||||
/// # let mut bucket_one = Vec::new();
|
||||
/// # let mut bucket_two = Vec::new();
|
||||
/// # append(&hasher, 42, &mut bucket_one, &mut bucket_two);
|
||||
/// # assert_eq!(bucket_one.len() + bucket_two.len(), 1);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
#[unstable(feature = "select_unpredictable", issue = "133962")]
|
||||
pub fn select_unpredictable<T>(self, true_val: T, false_val: T) -> T {
|
||||
crate::intrinsics::select_unpredictable(self, true_val, false_val)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1545,7 +1545,7 @@ pub const fn unlikely(b: bool) -> bool {
|
||||
/// Therefore, implementations must not require the user to uphold
|
||||
/// any safety invariants.
|
||||
///
|
||||
/// This intrinsic does not have a stable counterpart.
|
||||
/// The public form of this instrinsic is [`bool::select_unpredictable`].
|
||||
#[unstable(feature = "core_intrinsics", issue = "none")]
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_nounwind]
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
use crate::cmp::Ordering::{self, Equal, Greater, Less};
|
||||
use crate::intrinsics::{exact_div, select_unpredictable, unchecked_sub};
|
||||
use crate::intrinsics::{exact_div, unchecked_sub};
|
||||
use crate::mem::{self, SizedTypeProperties};
|
||||
use crate::num::NonZero;
|
||||
use crate::ops::{Bound, OneSidedRange, Range, RangeBounds, RangeInclusive};
|
||||
@@ -2835,7 +2835,7 @@ pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result<usize, usize>
|
||||
// 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);
|
||||
base = (cmp == Greater).select_unpredictable(base, mid);
|
||||
|
||||
// This is imprecise in the case where `size` is odd and the
|
||||
// comparison returns Greater: the mid element still gets included
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
//@ compile-flags: -O
|
||||
|
||||
#![feature(select_unpredictable)]
|
||||
#![crate_type = "lib"]
|
||||
|
||||
#[no_mangle]
|
||||
pub fn test_int(p: bool, a: u64, b: u64) -> u64 {
|
||||
// CHECK-LABEL: define{{.*}} @test_int
|
||||
// CHECK: select i1 %p, i64 %a, i64 %b, !unpredictable
|
||||
p.select_unpredictable(a, b)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn test_pair(p: bool, a: (u64, u64), b: (u64, u64)) -> (u64, u64) {
|
||||
// CHECK-LABEL: define{{.*}} @test_pair
|
||||
// CHECK: select i1 %p, {{.*}}, !unpredictable
|
||||
p.select_unpredictable(a, b)
|
||||
}
|
||||
|
||||
struct Large {
|
||||
e: [u64; 100],
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn test_struct(p: bool, a: Large, b: Large) -> Large {
|
||||
// CHECK-LABEL: define{{.*}} @test_struct
|
||||
// CHECK: select i1 %p, {{.*}}, !unpredictable
|
||||
p.select_unpredictable(a, b)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn test_zst(p: bool, a: (), b: ()) -> () {
|
||||
// CHECK-LABEL: define{{.*}} @test_zst
|
||||
p.select_unpredictable(a, b)
|
||||
}
|
||||
Reference in New Issue
Block a user