Skip to content

Commit 69942f0

Browse files
committed
core: implement bool::select_unpredictable
1 parent acf4842 commit 69942f0

File tree

2 files changed

+47
-1
lines changed

2 files changed

+47
-1
lines changed

library/core/src/bool.rs

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

library/core/src/intrinsics/mod.rs

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

0 commit comments

Comments
 (0)