use super::Prefetcher;
use crate::common::VirtAddr;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Direction {
None,
Ascending,
Descending,
}
#[derive(Debug)]
pub struct StreamPrefetcher {
line_bytes: u64,
degree: usize,
last_addr: u64,
direction: Direction,
confidence: u8,
}
impl StreamPrefetcher {
pub const fn new(line_bytes: usize, degree: usize) -> Self {
Self {
line_bytes: line_bytes as u64,
degree: if degree == 0 { 1 } else { degree },
last_addr: 0,
direction: Direction::None,
confidence: 0,
}
}
}
impl Prefetcher for StreamPrefetcher {
fn observe(&mut self, addr: u64, _pc: Option<VirtAddr>, _hit: bool) -> Vec<u64> {
let mut prefetches = Vec::new();
let diff = (addr as i64) - (self.last_addr as i64);
let line_sz = self.line_bytes as i64;
let current_dir = if diff == line_sz {
Direction::Ascending
} else if diff == -line_sz {
Direction::Descending
} else {
Direction::None
};
if current_dir != Direction::None {
if current_dir == self.direction {
if self.confidence < 3 {
self.confidence += 1;
}
} else {
self.direction = current_dir;
self.confidence = 1;
}
} else if self.confidence > 0 {
self.confidence -= 1;
} else {
self.direction = Direction::None;
}
if self.confidence >= 2 {
for k in 1..=self.degree {
let offset = if self.direction == Direction::Ascending {
(k as u64) * self.line_bytes
} else {
((k as i64) * -(line_sz)) as u64
};
let target = (addr & !(self.line_bytes - 1)).wrapping_add(offset);
prefetches.push(target);
}
}
self.last_addr = addr;
prefetches
}
}