Skip to content

Commit 04029ed

Browse files
committed
Move select_unpredictable to the hint module
1 parent d1d4180 commit 04029ed

File tree

5 files changed

+60
-59
lines changed

5 files changed

+60
-59
lines changed

Diff for: core/src/bool.rs

-48
Original file line numberDiff line numberDiff line change
@@ -61,52 +61,4 @@ 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-
}
11264
}

Diff for: core/src/hint.rs

+49
Original file line numberDiff line numberDiff line change
@@ -734,3 +734,52 @@ pub const fn unlikely(b: bool) -> bool {
734734
pub const fn cold_path() {
735735
crate::intrinsics::cold_path()
736736
}
737+
738+
/// Returns either `true_val` or `false_val` depending on the value of `b`,
739+
/// with a hint to the compiler that `b` is unlikely to be correctly
740+
/// predicted by a CPU’s branch predictor.
741+
///
742+
/// This method is functionally equivalent to
743+
/// ```ignore (this is just for illustrative purposes)
744+
/// fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T {
745+
/// if b { true_val } else { false_val }
746+
/// }
747+
/// ```
748+
/// but might generate different assembly. In particular, on platforms with
749+
/// a conditional move or select instruction (like `cmov` on x86 or `csel`
750+
/// on ARM) the optimizer might use these instructions to avoid branches,
751+
/// which can benefit performance if the branch predictor is struggling
752+
/// with predicting `condition`, such as in an implementation of binary
753+
/// search.
754+
///
755+
/// Note however that this lowering is not guaranteed (on any platform) and
756+
/// should not be relied upon when trying to write constant-time code. Also
757+
/// be aware that this lowering might *decrease* performance if `condition`
758+
/// is well-predictable. It is advisable to perform benchmarks to tell if
759+
/// this function is useful.
760+
///
761+
/// # Examples
762+
///
763+
/// Distribute values evenly between two buckets:
764+
/// ```
765+
/// #![feature(select_unpredictable)]
766+
///
767+
/// use std::hash::BuildHasher;
768+
/// use std::hint;
769+
///
770+
/// fn append<H: BuildHasher>(hasher: &H, v: i32, bucket_one: &mut Vec<i32>, bucket_two: &mut Vec<i32>) {
771+
/// let hash = hasher.hash_one(&v);
772+
/// let bucket = hint::select_unpredictable(hash % 2 == 0, bucket_one, bucket_two);
773+
/// bucket.push(v);
774+
/// }
775+
/// # let hasher = std::collections::hash_map::RandomState::new();
776+
/// # let mut bucket_one = Vec::new();
777+
/// # let mut bucket_two = Vec::new();
778+
/// # append(&hasher, 42, &mut bucket_one, &mut bucket_two);
779+
/// # assert_eq!(bucket_one.len() + bucket_two.len(), 1);
780+
/// ```
781+
#[inline(always)]
782+
#[unstable(feature = "select_unpredictable", issue = "133962")]
783+
pub fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T {
784+
crate::intrinsics::select_unpredictable(b, true_val, false_val)
785+
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1326,7 +1326,7 @@ pub const fn unlikely(b: bool) -> bool {
13261326
/// Therefore, implementations must not require the user to uphold
13271327
/// any safety invariants.
13281328
///
1329-
/// The public form of this instrinsic is [`bool::select_unpredictable`].
1329+
/// The public form of this instrinsic is [`core::hint::select_unpredictable`].
13301330
#[unstable(feature = "core_intrinsics", issue = "none")]
13311331
#[rustc_intrinsic]
13321332
#[rustc_nounwind]

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -2828,7 +2828,7 @@ impl<T> [T] {
28282828
// Binary search interacts poorly with branch prediction, so force
28292829
// the compiler to use conditional moves if supported by the target
28302830
// architecture.
2831-
base = (cmp == Greater).select_unpredictable(base, mid);
2831+
base = hint::select_unpredictable(cmp == Greater, base, mid);
28322832

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

Diff for: core/src/slice/sort/shared/smallsort.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use crate::mem::{self, ManuallyDrop, MaybeUninit};
44
use crate::slice::sort::shared::FreezeMarker;
5-
use crate::{intrinsics, ptr, slice};
5+
use crate::{hint, intrinsics, ptr, slice};
66

77
// It's important to differentiate between SMALL_SORT_THRESHOLD performance for
88
// small slices and small-sort performance sorting small sub-slices as part of
@@ -408,8 +408,8 @@ where
408408
// }
409409

410410
// The goal is to generate cmov instructions here.
411-
let v_a_swap = should_swap.select_unpredictable(v_b, v_a);
412-
let v_b_swap = should_swap.select_unpredictable(v_a, v_b);
411+
let v_a_swap = hint::select_unpredictable(should_swap, v_b, v_a);
412+
let v_b_swap = hint::select_unpredictable(should_swap, v_a, v_b);
413413

414414
let v_b_swap_tmp = ManuallyDrop::new(ptr::read(v_b_swap));
415415
ptr::copy(v_a_swap, v_a, 1);
@@ -640,15 +640,15 @@ pub unsafe fn sort4_stable<T, F: FnMut(&T, &T) -> bool>(
640640
// 1, 1 | c b a d
641641
let c3 = is_less(&*c, &*a);
642642
let c4 = is_less(&*d, &*b);
643-
let min = c3.select_unpredictable(c, a);
644-
let max = c4.select_unpredictable(b, d);
645-
let unknown_left = c3.select_unpredictable(a, c4.select_unpredictable(c, b));
646-
let unknown_right = c4.select_unpredictable(d, c3.select_unpredictable(b, c));
643+
let min = hint::select_unpredictable(c3, c, a);
644+
let max = hint::select_unpredictable(c4, b, d);
645+
let unknown_left = hint::select_unpredictable(c3, a, hint::select_unpredictable(c4, c, b));
646+
let unknown_right = hint::select_unpredictable(c4, d, hint::select_unpredictable(c3, b, c));
647647

648648
// Sort the last two unknown elements.
649649
let c5 = is_less(&*unknown_right, &*unknown_left);
650-
let lo = c5.select_unpredictable(unknown_right, unknown_left);
651-
let hi = c5.select_unpredictable(unknown_left, unknown_right);
650+
let lo = hint::select_unpredictable(c5, unknown_right, unknown_left);
651+
let hi = hint::select_unpredictable(c5, unknown_left, unknown_right);
652652

653653
ptr::copy_nonoverlapping(min, dst, 1);
654654
ptr::copy_nonoverlapping(lo, dst.add(1), 1);

0 commit comments

Comments
 (0)