Skip to content

Commit d938ba2

Browse files
matthiaskrgrgitbot
authored and
gitbot
committed
Rollup merge of rust-lang#133964 - joboet:select_unpredictable, r=tgross35
core: implement `bool::select_unpredictable` Tracking issue: rust-lang#133962 ACP: rust-lang/libs-team#468
2 parents c79c719 + eb900b2 commit d938ba2

File tree

3 files changed

+51
-3
lines changed

3 files changed

+51
-3
lines changed

Diff for: core/src/bool.rs

+48
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,52 @@ impl bool {
6161
pub fn then<T, F: FnOnce() -> T>(self, f: F) -> Option<T> {
6262
if self { Some(f()) } else { None }
6363
}
64+
65+
/// Returns either `true_val` or `false_val` depending on the value of
66+
/// `self`, with a hint to the compiler that `self` is unlikely
67+
/// to be correctly predicted by a CPU’s branch predictor.
68+
///
69+
/// This method is functionally equivalent to
70+
/// ```ignore (this is just for illustrative purposes)
71+
/// fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T {
72+
/// if b { true_val } else { false_val }
73+
/// }
74+
/// ```
75+
/// but might generate different assembly. In particular, on platforms with
76+
/// a conditional move or select instruction (like `cmov` on x86 or `csel`
77+
/// on ARM) the optimizer might use these instructions to avoid branches,
78+
/// which can benefit performance if the branch predictor is struggling
79+
/// with predicting `condition`, such as in an implementation of binary
80+
/// search.
81+
///
82+
/// Note however that this lowering is not guaranteed (on any platform) and
83+
/// should not be relied upon when trying to write constant-time code. Also
84+
/// be aware that this lowering might *decrease* performance if `condition`
85+
/// is well-predictable. It is advisable to perform benchmarks to tell if
86+
/// this function is useful.
87+
///
88+
/// # Examples
89+
///
90+
/// Distribute values evenly between two buckets:
91+
/// ```
92+
/// #![feature(select_unpredictable)]
93+
///
94+
/// use std::hash::BuildHasher;
95+
///
96+
/// fn append<H: BuildHasher>(hasher: &H, v: i32, bucket_one: &mut Vec<i32>, bucket_two: &mut Vec<i32>) {
97+
/// let hash = hasher.hash_one(&v);
98+
/// let bucket = (hash % 2 == 0).select_unpredictable(bucket_one, bucket_two);
99+
/// bucket.push(v);
100+
/// }
101+
/// # let hasher = std::collections::hash_map::RandomState::new();
102+
/// # let mut bucket_one = Vec::new();
103+
/// # let mut bucket_two = Vec::new();
104+
/// # append(&hasher, 42, &mut bucket_one, &mut bucket_two);
105+
/// # assert_eq!(bucket_one.len() + bucket_two.len(), 1);
106+
/// ```
107+
#[inline(always)]
108+
#[unstable(feature = "select_unpredictable", issue = "133962")]
109+
pub fn select_unpredictable<T>(self, true_val: T, false_val: T) -> T {
110+
crate::intrinsics::select_unpredictable(self, true_val, false_val)
111+
}
64112
}

Diff for: core/src/intrinsics/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1545,7 +1545,7 @@ pub const fn unlikely(b: bool) -> bool {
15451545
/// Therefore, implementations must not require the user to uphold
15461546
/// any safety invariants.
15471547
///
1548-
/// This intrinsic does not have a stable counterpart.
1548+
/// The public form of this instrinsic is [`bool::select_unpredictable`].
15491549
#[unstable(feature = "core_intrinsics", issue = "none")]
15501550
#[rustc_intrinsic]
15511551
#[rustc_nounwind]

Diff for: core/src/slice/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#![stable(feature = "rust1", since = "1.0.0")]
88

99
use crate::cmp::Ordering::{self, Equal, Greater, Less};
10-
use crate::intrinsics::{exact_div, select_unpredictable, unchecked_sub};
10+
use crate::intrinsics::{exact_div, unchecked_sub};
1111
use crate::mem::{self, SizedTypeProperties};
1212
use crate::num::NonZero;
1313
use crate::ops::{Bound, OneSidedRange, Range, RangeBounds, RangeInclusive};
@@ -2835,7 +2835,7 @@ impl<T> [T] {
28352835
// Binary search interacts poorly with branch prediction, so force
28362836
// the compiler to use conditional moves if supported by the target
28372837
// architecture.
2838-
base = select_unpredictable(cmp == Greater, base, mid);
2838+
base = (cmp == Greater).select_unpredictable(base, mid);
28392839

28402840
// This is imprecise in the case where `size` is odd and the
28412841
// comparison returns Greater: the mid element still gets included

0 commit comments

Comments
 (0)