use crate::common::PAGE_SHIFT;
#[derive(Clone, Copy, Debug)]
struct StoreRun {
page: u64,
last_line: u64,
direction: i64,
ahead: Option<u64>,
}
#[derive(Debug)]
pub struct StoreStreamPrefetcher {
runs: Vec<StoreRun>,
capacity: usize,
l2_lines: usize,
line_bytes: u64,
}
impl StoreStreamPrefetcher {
#[must_use]
pub fn new(line_bytes: usize, streams: usize, l2_lines: usize) -> Self {
Self {
runs: Vec::with_capacity(streams.max(1)),
capacity: streams.max(1),
l2_lines,
line_bytes: line_bytes as u64,
}
}
pub fn observe(&mut self, addr: u64) -> Vec<u64> {
let line = addr & !(self.line_bytes - 1);
let page = addr >> PAGE_SHIFT;
let adjacent = self
.runs
.iter()
.position(|run| run.page == page && line.abs_diff(run.last_line) == self.line_bytes);
let Some(index) = adjacent else {
self.start_run(page, line);
return Vec::new();
};
let mut run = self.runs.remove(index);
let direction = if line > run.last_line { 1 } else { -1 };
let confirmed = run.direction == direction;
if !confirmed {
run.direction = direction;
run.ahead = None;
}
run.last_line = line;
let prefetches = if confirmed { self.extend(&mut run) } else { Vec::new() };
self.runs.push(run);
prefetches
}
fn start_run(&mut self, page: u64, line: u64) {
if self.runs.len() == self.capacity {
let _ = self.runs.remove(0);
}
self.runs.push(StoreRun { page, last_line: line, direction: 0, ahead: None });
}
fn extend(&self, run: &mut StoreRun) -> Vec<u64> {
let step = self.line_bytes as i64 * run.direction;
let mut prefetches = Vec::new();
for k in 1..=self.l2_lines as i64 {
let target = (run.last_line as i64).wrapping_add(step * k) as u64;
if target >> PAGE_SHIFT != run.page {
break;
}
let requested = run.ahead.is_some_and(|ahead| {
(target as i64).wrapping_sub(ahead as i64).signum() != run.direction
});
if !requested {
prefetches.push(target);
run.ahead = Some(target);
}
}
prefetches
}
}