diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 71acede7e3eb3..e2ac746d31497 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -823,3 +823,152 @@ where crate::intrinsics::select_unpredictable(condition, true_val, false_val).assume_init() } } + +/// The expected temporal locality of a memory prefetch operation. +/// +/// Locality expresses how likely the prefetched data is to be reused soon, +/// and therefore which level of cache it should be brought into. +/// +/// The locality is just a hint, and may be ignored on some targets or by the hardware. +/// +/// Used with functions like [`prefetch_read`] and [`prefetch_write`]. +/// +/// [`prefetch_read`]: crate::hint::prefetch_read +/// [`prefetch_write`]: crate::hint::prefetch_write +#[unstable(feature = "hint_prefetch", issue = "146941")] +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Locality { + /// Data is expected to be reused eventually. + /// + /// Typically prefetches into L3 cache (if the CPU supports it). + L3, + /// Data is expected to be reused in the near future. + /// + /// Typically prefetches into L2 cache. + L2, + /// Data is expected to be reused very soon. + /// + /// Typically prefetches into L1 cache. + L1, +} + +impl Locality { + /// Convert to the constant that LLVM associates with a locality. + const fn to_llvm(self) -> i32 { + match self { + Self::L3 => 1, + Self::L2 => 2, + Self::L1 => 3, + } + } +} + +/// Prefetch the cache line containing `ptr` for a future read. +/// +/// A strategically placed prefetch can reduce cache miss latency if the data is accessed +/// soon after, but may also increase bandwidth usage or evict other cache lines. +/// +/// A prefetch is a *hint*, and may be ignored on certain targets or by the hardware. +/// +/// Passing a dangling or invalid pointer is permitted: the memory will not +/// actually be dereferenced, and no faults are raised. +/// +/// # Examples +/// +/// ``` +/// #![feature(hint_prefetch)] +/// use std::hint::{Locality, prefetch_read}; +/// use std::mem::size_of_val; +/// +/// // Prefetch all of `slice` into the L1 cache. +/// fn prefetch_slice(slice: &[T]) { +/// // On most systems the cache line size is 64 bytes. +/// for offset in (0..size_of_val(slice)).step_by(64) { +/// prefetch_read(slice.as_ptr().wrapping_add(offset), Locality::L1); +/// } +/// } +/// ``` +#[inline(always)] +#[unstable(feature = "hint_prefetch", issue = "146941")] +pub const fn prefetch_read(ptr: *const T, locality: Locality) { + match locality { + Locality::L3 => intrinsics::prefetch_read_data::(ptr), + Locality::L2 => intrinsics::prefetch_read_data::(ptr), + Locality::L1 => intrinsics::prefetch_read_data::(ptr), + } +} + +/// Prefetch the cache line containing `ptr` for a single future read, but attempt to avoid +/// polluting the cache. +/// +/// A strategically placed prefetch can reduce cache miss latency if the data is accessed +/// soon after, but may also increase bandwidth usage or evict other cache lines. +/// +/// A prefetch is a *hint*, and may be ignored on certain targets or by the hardware. +/// +/// Passing a dangling or invalid pointer is permitted: the memory will not +/// actually be dereferenced, and no faults are raised. +#[inline(always)] +#[unstable(feature = "hint_prefetch", issue = "146941")] +pub const fn prefetch_read_non_temporal(ptr: *const T, locality: Locality) { + // The LLVM intrinsic does not currently support specifying the locality. + let _ = locality; + intrinsics::prefetch_read_data::(ptr) +} + +/// Prefetch the cache line containing `ptr` for a future write. +/// +/// A strategically placed prefetch can reduce cache miss latency if the data is accessed +/// soon after, but may also increase bandwidth usage or evict other cache lines. +/// +/// A prefetch is a *hint*, and may be ignored on certain targets or by the hardware. +/// +/// Passing a dangling or invalid pointer is permitted: the memory will not +/// actually be dereferenced, and no faults are raised. +#[inline(always)] +#[unstable(feature = "hint_prefetch", issue = "146941")] +pub const fn prefetch_write(ptr: *mut T, locality: Locality) { + match locality { + Locality::L3 => intrinsics::prefetch_write_data::(ptr), + Locality::L2 => intrinsics::prefetch_write_data::(ptr), + Locality::L1 => intrinsics::prefetch_write_data::(ptr), + } +} + +/// Prefetch the cache line containing `ptr` for a single future write, but attempt to avoid +/// polluting the cache. +/// +/// A strategically placed prefetch can reduce cache miss latency if the data is accessed +/// soon after, but may also increase bandwidth usage or evict other cache lines. +/// +/// A prefetch is a *hint*, and may be ignored on certain targets or by the hardware. +/// +/// Passing a dangling or invalid pointer is permitted: the memory will not +/// actually be dereferenced, and no faults are raised. +#[inline(always)] +#[unstable(feature = "hint_prefetch", issue = "146941")] +pub const fn prefetch_write_non_temporal(ptr: *const T, locality: Locality) { + // The LLVM intrinsic does not currently support specifying the locality. + let _ = locality; + intrinsics::prefetch_write_data::(ptr) +} + +/// Prefetch the cache line containing `ptr` into the instruction cache for a future read. +/// +/// A strategically placed prefetch can reduce cache miss latency if the instructions are +/// accessed soon after, but may also increase bandwidth usage or evict other cache lines. +/// +/// A prefetch is a *hint*, and may be ignored on certain targets or by the hardware. +/// +/// Passing a dangling or invalid pointer is permitted: the memory will not +/// actually be dereferenced, and no faults are raised. +#[inline(always)] +#[unstable(feature = "hint_prefetch", issue = "146941")] +pub const fn prefetch_read_instruction(ptr: *const T, locality: Locality) { + match locality { + Locality::L3 => intrinsics::prefetch_read_instruction::(ptr), + Locality::L2 => intrinsics::prefetch_read_instruction::(ptr), + Locality::L1 => intrinsics::prefetch_read_instruction::(ptr), + } +}