1use std::sync::LazyLock;
2
3pub static PAGE_SIZE: LazyLock<usize> = LazyLock::new(|| {
4 #[cfg(target_family = "unix")]
5 unsafe {
6 libc::sysconf(libc::_SC_PAGESIZE) as usize
7 }
8 #[cfg(not(target_family = "unix"))]
9 {
10 4096
11 }
12});
13
14pub mod prefetch {
15 use super::PAGE_SIZE;
16
17 #[inline]
21 unsafe fn prefetch_l2_impl(ptr: *const u8) {
22 _ = ptr; #[cfg(target_arch = "x86_64")]
25 {
26 use std::arch::x86_64::*;
27 unsafe { _mm_prefetch(ptr as *const _, _MM_HINT_T1) };
28 }
29
30 #[cfg(all(target_arch = "aarch64", feature = "nightly"))]
31 {
32 use std::arch::aarch64::*;
33 unsafe { _prefetch(ptr as *const _, _PREFETCH_READ, _PREFETCH_LOCALITY2) };
34 }
35 }
36
37 #[inline(always)]
40 pub fn prefetch_l1(ptr: *const u8) {
41 _ = ptr; #[cfg(target_arch = "x86_64")]
44 {
45 use std::arch::x86_64::*;
46 unsafe { _mm_prefetch::<_MM_HINT_T0>(ptr as *const _) };
47 }
48
49 #[cfg(all(target_arch = "aarch64", feature = "nightly"))]
50 {
51 use std::arch::aarch64::*;
52 unsafe { _prefetch(ptr as *const _, _PREFETCH_READ, _PREFETCH_LOCALITY3) };
53 }
54 }
55
56 pub fn prefetch_l2(slice: &[u8]) {
58 if slice.is_empty() {
59 return;
60 }
61
62 for i in (0..slice.len()).step_by(*PAGE_SIZE) {
67 unsafe { prefetch_l2_impl(slice[i..].as_ptr()) };
68 }
69
70 unsafe { prefetch_l2_impl(slice[slice.len() - 1..].as_ptr()) }
71 }
72
73 pub fn madvise_sequential(#[allow(unused)] slice: &[u8]) {
75 #[cfg(target_family = "unix")]
76 madvise(slice, libc::MADV_SEQUENTIAL);
77 }
78
79 pub fn madvise_willneed(#[allow(unused)] slice: &[u8]) {
81 #[cfg(target_family = "unix")]
82 madvise(slice, libc::MADV_WILLNEED);
83 }
84
85 pub fn madvise_populate_read(#[allow(unused)] slice: &[u8]) {
87 #[cfg(target_os = "linux")]
88 madvise(slice, libc::MADV_POPULATE_READ);
89 }
90
91 pub fn force_populate_read(slice: &[u8]) {
93 for i in (0..slice.len()).step_by(*PAGE_SIZE) {
94 std::hint::black_box(slice[i]);
95 }
96
97 std::hint::black_box(slice.last().copied());
98 }
99
100 #[cfg(target_family = "unix")]
101 fn madvise(slice: &[u8], advice: libc::c_int) {
102 if slice.is_empty() {
103 return;
104 }
105 let ptr = slice.as_ptr();
106
107 let align = ptr as usize % *PAGE_SIZE;
108 let ptr = ptr.wrapping_sub(align);
109 let len = slice.len() + align;
110
111 if unsafe { libc::madvise(ptr as *mut libc::c_void, len, advice) } != 0 {
112 let err = std::io::Error::last_os_error();
113 if let std::io::ErrorKind::InvalidInput = err.kind() {
114 panic!("{}", err);
115 }
116 }
117 }
118
119 pub fn no_prefetch(_: &[u8]) {}
120
121 pub fn get_memory_prefetch_func(verbose: bool) -> fn(&[u8]) -> () {
123 let memory_prefetch_func = match std::env::var("POLARS_MEMORY_PREFETCH").ok().as_deref() {
124 None => {
125 #[cfg(target_family = "unix")]
128 {
129 madvise_willneed
130 }
131 #[cfg(not(target_family = "unix"))]
132 {
133 no_prefetch
134 }
135 },
136 Some("no_prefetch") => no_prefetch,
137 Some("prefetch_l2") => prefetch_l2,
138 Some("madvise_sequential") => {
139 #[cfg(target_family = "unix")]
140 {
141 madvise_sequential
142 }
143 #[cfg(not(target_family = "unix"))]
144 {
145 panic!(
146 "POLARS_MEMORY_PREFETCH=madvise_sequential is not supported by this system"
147 );
148 }
149 },
150 Some("madvise_willneed") => {
151 #[cfg(target_family = "unix")]
152 {
153 madvise_willneed
154 }
155 #[cfg(not(target_family = "unix"))]
156 {
157 panic!(
158 "POLARS_MEMORY_PREFETCH=madvise_willneed is not supported by this system"
159 );
160 }
161 },
162 Some("madvise_populate_read") => {
163 #[cfg(target_os = "linux")]
164 {
165 madvise_populate_read
166 }
167 #[cfg(not(target_os = "linux"))]
168 {
169 panic!(
170 "POLARS_MEMORY_PREFETCH=madvise_populate_read is not supported by this system"
171 );
172 }
173 },
174 Some("force_populate_read") => force_populate_read,
175 Some(v) => panic!("invalid value for POLARS_MEMORY_PREFETCH: {v}"),
176 };
177
178 if verbose {
179 let func_name = match memory_prefetch_func as usize {
180 v if v == no_prefetch as *const () as usize => "no_prefetch",
181 v if v == prefetch_l2 as *const () as usize => "prefetch_l2",
182 v if v == madvise_sequential as *const () as usize => "madvise_sequential",
183 v if v == madvise_willneed as *const () as usize => "madvise_willneed",
184 v if v == madvise_populate_read as *const () as usize => "madvise_populate_read",
185 v if v == force_populate_read as *const () as usize => "force_populate_read",
186 _ => unreachable!(),
187 };
188
189 eprintln!("memory prefetch function: {func_name}");
190 }
191
192 memory_prefetch_func
193 }
194}