1#![cfg_attr(
7 all(feature = "aarch64", target_arch = "aarch64"),
8 feature(stdarch_aarch64_prefetch)
9)]
10
11#[inline(always)]
26pub fn prefetch_index<T>(data: impl AsRef<[T]>, index: usize) {
27 let ptr = data.as_ref().as_ptr().wrapping_add(index) as *const i8;
28 #[cfg(all(target_arch = "x86_64", target_feature = "sse"))]
29 unsafe {
30 std::arch::x86_64::_mm_prefetch(ptr, std::arch::x86_64::_MM_HINT_T0);
31 }
32 #[cfg(all(target_arch = "x86", target_feature = "sse"))]
33 unsafe {
34 std::arch::x86::_mm_prefetch(ptr, std::arch::x86::_MM_HINT_T0);
35 }
36 #[cfg(all(target_arch = "aarch64", feature = "aarch64"))]
37 unsafe {
38 std::arch::aarch64::_prefetch::<
39 { std::arch::aarch64::_PREFETCH_READ },
40 { std::arch::aarch64::_PREFETCH_LOCALITY3 },
41 >(ptr);
42 }
43 #[cfg(all(target_arch = "aarch64", not(feature = "aarch64")))]
44 unsafe {
45 core::arch::asm!(
46 "prfm pldl1keep, [{p}]",
47 p = in(reg) ptr,
48 options(nostack, preserves_flags, readonly),
49 );
50 }
51 #[cfg(not(any(
52 all(target_arch = "x86_64", target_feature = "sse"),
53 all(target_arch = "x86", target_feature = "sse"),
54 all(target_arch = "aarch64")
55 )))]
56 {
57 let _ = ptr; }
59}
60
61#[inline(always)]
69pub fn prefetch_index_nta<T>(data: impl AsRef<[T]>, index: usize) {
70 let ptr = data.as_ref().as_ptr().wrapping_add(index) as *const i8;
71 #[cfg(all(target_arch = "x86_64", target_feature = "sse"))]
72 unsafe {
73 std::arch::x86_64::_mm_prefetch(ptr, std::arch::x86_64::_MM_HINT_NTA);
74 }
75 #[cfg(all(target_arch = "x86", target_feature = "sse"))]
76 unsafe {
77 std::arch::x86::_mm_prefetch(ptr, std::arch::x86::_MM_HINT_NTA);
78 }
79 #[cfg(all(target_arch = "aarch64", feature = "aarch64"))]
80 unsafe {
81 std::arch::aarch64::_prefetch::<
82 { std::arch::aarch64::_PREFETCH_READ },
83 { std::arch::aarch64::_PREFETCH_LOCALITY0 },
84 >(ptr);
85 }
86 #[cfg(all(target_arch = "aarch64", not(feature = "aarch64")))]
87 unsafe {
88 core::arch::asm!(
89 "prfm pldl1strm, [{p}]",
90 p = in(reg) ptr,
91 options(nostack, preserves_flags, readonly),
92 );
93 }
94 #[cfg(not(any(
95 all(target_arch = "x86_64", target_feature = "sse"),
96 all(target_arch = "x86", target_feature = "sse"),
97 all(target_arch = "aarch64")
98 )))]
99 {
100 let _ = ptr; }
102}
103
104#[cfg(test)]
105mod tests {
106 use std::{array::from_fn, hint::black_box};
107
108 use super::*;
109
110 #[test]
111 fn basic() {
112 let data = vec![0u8; 1024];
113 prefetch_index(&data, 512);
114 prefetch_index_nta(&data, 512);
115 }
116
117 fn setup() -> Vec<u64> {
119 let len = 1024 * 1024 * 1024 / 8;
120 let mut data = vec![0u64; len];
121 let mut idx = 0;
124 for i in 0..len {
125 let next = (idx + (i + 1)) % len;
126 assert_eq!(data[idx], 0);
127 data[idx] = next as u64;
128 idx = next;
129 }
130 data
131 }
132
133 #[test]
134 fn out_of_bounds_is_ok() {
135 for j in 0..20 {
136 let len = 1 << j;
137 let data = vec![0u64; len];
138 for i in 0..100usize {
139 prefetch_index(&data, i.wrapping_neg());
140 prefetch_index(&data, len + i);
141 }
142 prefetch_index(&data, usize::MAX);
143 prefetch_index(&data, len + usize::MAX / 2);
144 }
145 }
146
147 #[test]
148 fn batched_pointer_chasing_prefetch() {
149 const B: usize = 32;
151
152 let data = setup();
153
154 let time = |prefetch: bool| {
155 let mut indices: [usize; B] = from_fn(|i| i * data.len() / B + i);
156 let start = std::time::Instant::now();
157 let mut sum = 0;
158 for _ in 0..data.len() / B {
159 for idx in &mut indices {
160 *idx = data[*idx] as usize;
161 if prefetch {
162 prefetch_index(&data, *idx);
163 }
164 let mut x: usize = 1;
166 for _ in 0..16 {
167 x = x.wrapping_mul(*idx);
168 sum += x;
169 }
170 }
171 }
172 black_box(sum);
173 let duration = start.elapsed();
174 let ns_per_it = duration.as_nanos() as f64 / (data.len() as f64);
175 eprintln!(
176 "Prefetch={prefetch:<5}: {:?} {:5.2} ns/it",
177 duration, ns_per_it
178 );
179 ns_per_it
180 };
181 let no_prefetch = time(false);
182 let with_prefetch = time(true);
183 assert!(with_prefetch < 0.8 * no_prefetch, "Time with prefetching {with_prefetch} is not sufficiently smaller than time without prefetching {no_prefetch}");
184 }
185}