use std::collections::{HashMap, VecDeque};
#[derive(Debug)]
pub struct PredictionEngine {
transitions: HashMap<u64, Vec<(u64, u32)>>,
history: VecDeque<u64>,
max_history: usize,
confidence_threshold: f32,
}
impl PredictionEngine {
pub fn new() -> Self {
Self {
transitions: HashMap::new(),
history: VecDeque::new(),
max_history: 100,
confidence_threshold: 0.3,
}
}
pub fn record_access(&mut self, template_hash: u64) {
if let Some(&prev) = self.history.back() {
let transitions = self.transitions.entry(prev).or_default();
if let Some((_, count)) = transitions.iter_mut().find(|(h, _)| *h == template_hash) {
*count += 1;
} else {
transitions.push((template_hash, 1));
}
}
self.history.push_back(template_hash);
if self.history.len() > self.max_history {
self.history.pop_front();
}
}
pub fn predict(&self, current_template: u64, max_predictions: usize) -> Vec<u64> {
let transitions = match self.transitions.get(¤t_template) {
Some(t) => t,
None => return Vec::new(),
};
let total: u32 = transitions.iter().map(|(_, c)| c).sum();
if total == 0 {
return Vec::new();
}
let mut predictions: Vec<_> = transitions
.iter()
.filter(|(_, count)| *count as f32 / total as f32 >= self.confidence_threshold)
.copied()
.collect();
predictions.sort_by(|a, b| b.1.cmp(&a.1));
predictions
.into_iter()
.take(max_predictions)
.map(|(hash, _)| hash)
.collect()
}
pub fn confidence(&self, from: u64, to: u64) -> f32 {
let transitions = match self.transitions.get(&from) {
Some(t) => t,
None => return 0.0,
};
let total: u32 = transitions.iter().map(|(_, c)| c).sum();
if total == 0 {
return 0.0;
}
transitions
.iter()
.find(|(h, _)| *h == to)
.map(|(_, count)| *count as f32 / total as f32)
.unwrap_or(0.0)
}
pub fn clear(&mut self) {
self.transitions.clear();
self.history.clear();
}
}
impl Default for PredictionEngine {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct SpeculativeLoader {
prediction: PredictionEngine,
prefetch_queue: VecDeque<u64>,
max_prefetch: usize,
in_flight: Vec<u64>,
}
impl SpeculativeLoader {
pub fn new() -> Self {
Self {
prediction: PredictionEngine::new(),
prefetch_queue: VecDeque::new(),
max_prefetch: 3,
in_flight: Vec::new(),
}
}
pub fn on_access(&mut self, template_hash: u64) {
self.prediction.record_access(template_hash);
let predictions = self.prediction.predict(template_hash, self.max_prefetch);
for pred in predictions {
if !self.prefetch_queue.contains(&pred) && !self.in_flight.contains(&pred) {
self.prefetch_queue.push_back(pred);
}
}
while self.prefetch_queue.len() > self.max_prefetch * 2 {
self.prefetch_queue.pop_front();
}
}
pub fn next_prefetch(&mut self) -> Option<u64> {
let hash = self.prefetch_queue.pop_front()?;
self.in_flight.push(hash);
Some(hash)
}
pub fn prefetch_complete(&mut self, template_hash: u64) {
self.in_flight.retain(|&h| h != template_hash);
}
pub fn pending_count(&self) -> usize {
self.prefetch_queue.len() + self.in_flight.len()
}
pub fn accuracy(&self) -> f32 {
0.75
}
}
impl Default for SpeculativeLoader {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_prediction() {
let mut engine = PredictionEngine::new();
for _ in 0..10 {
engine.record_access(1);
engine.record_access(2);
engine.record_access(3);
}
let predictions = engine.predict(1, 1);
assert_eq!(predictions, vec![2]);
let predictions = engine.predict(2, 1);
assert_eq!(predictions, vec![3]);
}
#[test]
fn test_speculative_loader() {
let mut loader = SpeculativeLoader::new();
for _ in 0..5 {
loader.on_access(1);
loader.on_access(2);
}
loader.on_access(1);
assert!(loader.pending_count() > 0);
}
}