Skip to main content

petaplot_core/compute/
prefetcher.rs

1use std::collections::VecDeque;
2use std::time::Instant;
3use crate::error::Result;
4use crate::storage::mmap_reader::MmapReader;
5
6/// Solicitud de precarga especulativa para el predictor de navegación.
7#[derive(Debug, Clone, PartialEq)]
8pub struct PrefetchRequest {
9    pub offset_bytes: usize,
10    pub length_bytes: usize,
11    pub target_time_start: f64,
12    pub target_time_end: f64,
13}
14
15/// Predictor de navegación de alta velocidad para la navegación sin latencia (*zero-stutter*).
16pub struct SpeculativePrefetcher {
17    last_center_time: f64,
18    last_update: Instant,
19    current_velocity: f64, // Muestras/segundo o Unidades de tiempo/segundo
20    prediction_window_secs: f64,
21    cache_capacity: usize,
22    lru_cache: VecDeque<PrefetchRequest>,
23}
24
25impl Default for SpeculativePrefetcher {
26    fn default() -> Self {
27        Self {
28            last_center_time: 0.0,
29            last_update: Instant::now(),
30            current_velocity: 0.0,
31            prediction_window_secs: 0.200, // 200 ms de ventana predictiva
32            cache_capacity: 16,
33            lru_cache: VecDeque::new(),
34        }
35    }
36}
37
38impl SpeculativePrefetcher {
39    pub fn new(prediction_window_secs: f64, cache_capacity: usize) -> Self {
40        Self {
41            prediction_window_secs,
42            cache_capacity,
43            ..Default::default()
44        }
45    }
46
47    /// Actualiza el estado de navegación según la posición actual del viewport.
48    /// Calcula la velocidad $\vec{v} = \frac{\Delta x}{\Delta t}$ e inercia.
49    pub fn update_position(&mut self, current_center_time: f64) {
50        let now = Instant::now();
51        let dt = now.duration_since(self.last_update).as_secs_f64();
52
53        if dt > 0.001 {
54            let dx = current_center_time - self.last_center_time;
55            let instant_velocity = dx / dt;
56
57            // Filtro de suavizado exponencial para la velocidad (alpha = 0.4)
58            self.current_velocity = 0.6 * self.current_velocity + 0.4 * instant_velocity;
59            self.last_center_time = current_center_time;
60            self.last_update = now;
61        }
62    }
63
64    /// Obtiene la velocidad actual estimada del desplazamiento.
65    pub fn current_velocity(&self) -> f64 {
66        self.current_velocity
67    }
68
69    /// Calcula la posición futura predecida $T_{\text{futuro}} = T_{\text{actual}} + \vec{v} \cdot \Delta t_{\text{preview}}$.
70    pub fn predict_future_range(&self, current_span: f64) -> (f64, f64) {
71        let delta_t = self.current_velocity * self.prediction_window_secs;
72        let future_center = self.last_center_time + delta_t;
73        let half_span = current_span / 2.0;
74
75        (future_center - half_span, future_center + half_span)
76    }
77
78    /// Emite la solicitud de precarga al kernel usando `madvise` (`Advice::WillNeed`).
79    pub fn prefetch_mmap(&mut self, reader: &MmapReader, offset_bytes: usize, length_bytes: usize) -> Result<()> {
80        let req = PrefetchRequest {
81            offset_bytes,
82            length_bytes,
83            target_time_start: 0.0,
84            target_time_end: 0.0,
85        };
86
87        if self.lru_cache.contains(&req) {
88            return Ok(());
89        }
90
91        // Informar al Kernel del sistema operativo
92        reader.advise_will_need(offset_bytes, length_bytes)?;
93
94        // Actualizar caché LRU
95        if self.lru_cache.len() >= self.cache_capacity {
96            self.lru_cache.pop_back();
97        }
98        self.lru_cache.push_front(req);
99
100        Ok(())
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107    use std::thread::sleep;
108    use std::time::Duration;
109
110    #[test]
111    fn test_velocity_prediction() {
112        let mut prefetcher = SpeculativePrefetcher::new(0.2, 10);
113
114        prefetcher.update_position(0.0);
115        sleep(Duration::from_millis(10));
116        prefetcher.update_position(10.0);
117
118        assert!(prefetcher.current_velocity() > 0.0);
119
120        let (future_start, future_end) = prefetcher.predict_future_range(5.0);
121        assert!(future_start > 7.5);
122        assert!(future_end > future_start);
123    }
124}