use std::fs::File;
use std::ops::Range;
use memmap2::Mmap;
pub(crate) struct PrefetchBatcher<'a> {
#[allow(dead_code)] file: &'a File,
mmap: &'a Mmap,
#[cfg(windows)]
ranges: Vec<Range<usize>>, }
impl<'a> PrefetchBatcher<'a> {
pub(crate) fn new(file: &'a File, mmap: &'a Mmap) -> Self {
Self {
file,
mmap,
#[cfg(windows)]
ranges: Vec::new(),
}
}
#[cfg(target_vendor = "apple")]
pub(crate) fn push(&mut self, range: Range<usize>) {
use std::os::unix::io::AsRawFd;
let Some(range) = clamp_range(range, self.mmap.len()) else {
return;
};
const MAX_HINT_LEN: usize = 1 << 30;
let fd = self.file.as_raw_fd();
for offset in range.clone().step_by(MAX_HINT_LEN) {
let len = (range.end - offset).min(MAX_HINT_LEN);
let radvisory = libc::radvisory { ra_offset: offset as libc::off_t, ra_count: len as libc::c_int };
_ = unsafe { libc::fcntl(fd, libc::F_RDADVISE, &radvisory) };
}
}
#[cfg(all(unix, not(target_vendor = "apple")))]
pub(crate) fn push(&mut self, range: Range<usize>) {
let Some(range) = clamp_range(range, self.mmap.len()) else {
return;
};
let _ = self.mmap.advise_range(memmap2::Advice::WillNeed, range.start, range.end - range.start);
}
#[cfg(windows)]
pub(crate) fn push(&mut self, range: Range<usize>) {
let Some(range) = clamp_range(range, self.mmap.len()) else {
return;
};
self.ranges.push(range);
}
#[cfg(not(any(unix, windows)))]
pub(crate) fn push(&mut self, _range: Range<usize>) {}
#[cfg(windows)]
pub(crate) fn flush(&mut self) {
use std::ffi::c_void;
use windows_sys::Win32::System::Memory::{PrefetchVirtualMemory, WIN32_MEMORY_RANGE_ENTRY};
use windows_sys::Win32::System::Threading::GetCurrentProcess;
let base = self.mmap.as_ptr();
let entries = self
.ranges
.iter()
.map(|range| WIN32_MEMORY_RANGE_ENTRY {
VirtualAddress: unsafe { base.add(range.start) } as *mut c_void,
NumberOfBytes: range.end - range.start,
})
.collect::<Vec<_>>();
self.ranges.clear();
unsafe { PrefetchVirtualMemory(GetCurrentProcess(), entries.len(), entries.as_ptr(), 0) };
}
#[cfg(not(windows))]
pub(crate) fn flush(&mut self) {}
}
fn clamp_range(range: Range<usize>, mmap_len: usize) -> Option<Range<usize>> {
if range.start < range.end && range.end <= mmap_len {
return Some(range);
}
let clamped = range.start.min(mmap_len)..range.end.min(mmap_len);
if clamped.start < clamped.end { Some(clamped) } else { None }
}
impl<'a> Drop for PrefetchBatcher<'a> {
fn drop(&mut self) {
self.flush();
}
}