1use std::collections::{BTreeMap, HashMap, VecDeque};
14use std::sync::Arc;
15use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
16use std::time::{Duration, Instant};
17
18#[cfg(feature = "async")]
19use tokio::sync::RwLock;
20
21use crate::error::Result;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
29pub enum AccessPattern {
30 SequentialForward,
32 SequentialBackward,
34 Strided { stride: i64 },
36 Random,
38 Spatial,
40 TemporalPeriodic { period_ms: u64 },
42 Burst,
44 #[default]
46 Unknown,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
51pub enum PrefetchPriority {
52 Critical = 4,
54 High = 3,
56 #[default]
58 Medium = 2,
59 Low = 1,
61 Background = 0,
63}
64
65#[derive(Debug, Clone)]
71pub struct AccessRecord {
72 pub key: String,
74 pub timestamp: Instant,
76 pub coordinates: Option<(usize, usize, usize)>,
78 pub size: Option<usize>,
80 pub latency: Option<Duration>,
82 pub cache_hit: bool,
84}
85
86impl AccessRecord {
87 #[must_use]
89 pub fn new(key: String) -> Self {
90 Self {
91 key,
92 timestamp: Instant::now(),
93 coordinates: None,
94 size: None,
95 latency: None,
96 cache_hit: false,
97 }
98 }
99
100 #[must_use]
102 pub fn with_coordinates(key: String, x: usize, y: usize, z: usize) -> Self {
103 Self {
104 key,
105 timestamp: Instant::now(),
106 coordinates: Some((x, y, z)),
107 size: None,
108 latency: None,
109 cache_hit: false,
110 }
111 }
112
113 #[must_use]
115 pub fn with_size(mut self, size: usize) -> Self {
116 self.size = Some(size);
117 self
118 }
119
120 #[must_use]
122 pub fn with_latency(mut self, latency: Duration) -> Self {
123 self.latency = Some(latency);
124 self
125 }
126
127 #[must_use]
129 pub fn with_cache_hit(mut self, hit: bool) -> Self {
130 self.cache_hit = hit;
131 self
132 }
133}
134
135#[derive(Debug, Clone)]
137pub struct PrefetchTarget {
138 pub key: String,
140 pub priority: PrefetchPriority,
142 pub coordinates: Option<(usize, usize, usize)>,
144 pub confidence: f64,
146 pub estimated_size: Option<usize>,
148}
149
150impl PrefetchTarget {
151 #[must_use]
153 pub fn new(key: String, priority: PrefetchPriority, confidence: f64) -> Self {
154 Self {
155 key,
156 priority,
157 coordinates: None,
158 confidence,
159 estimated_size: None,
160 }
161 }
162
163 #[must_use]
165 pub fn with_coordinates(mut self, x: usize, y: usize, z: usize) -> Self {
166 self.coordinates = Some((x, y, z));
167 self
168 }
169
170 #[must_use]
172 pub fn with_estimated_size(mut self, size: usize) -> Self {
173 self.estimated_size = Some(size);
174 self
175 }
176}
177
178#[derive(Debug)]
184pub struct TemporalPatternAnalyzer {
185 intervals: VecDeque<u64>,
187 max_intervals: usize,
189 detected_period: Option<u64>,
191 burst_threshold: f64,
193}
194
195impl TemporalPatternAnalyzer {
196 #[must_use]
198 pub fn new(max_intervals: usize) -> Self {
199 Self {
200 intervals: VecDeque::with_capacity(max_intervals),
201 max_intervals,
202 detected_period: None,
203 burst_threshold: 10.0, }
205 }
206
207 pub fn record_interval(&mut self, interval_ms: u64) {
209 if self.intervals.len() >= self.max_intervals {
210 self.intervals.pop_front();
211 }
212 self.intervals.push_back(interval_ms);
213 self.analyze_periodicity();
214 }
215
216 fn analyze_periodicity(&mut self) {
218 if self.intervals.len() < 5 {
219 self.detected_period = None;
220 return;
221 }
222
223 let sum: u64 = self.intervals.iter().sum();
225 let mean = sum as f64 / self.intervals.len() as f64;
226
227 let variance: f64 = self
228 .intervals
229 .iter()
230 .map(|&x| {
231 let diff = x as f64 - mean;
232 diff * diff
233 })
234 .sum::<f64>()
235 / self.intervals.len() as f64;
236
237 let std_dev = variance.sqrt();
238
239 let cv = std_dev / mean;
241 if cv < 0.3 && mean > 100.0 {
242 self.detected_period = Some(mean as u64);
244 } else {
245 self.detected_period = None;
246 }
247 }
248
249 #[must_use]
251 pub fn is_burst(&self) -> bool {
252 if self.intervals.len() < 3 {
253 return false;
254 }
255
256 let recent: Vec<_> = self.intervals.iter().rev().take(5).copied().collect();
258 if recent.is_empty() {
259 return false;
260 }
261
262 let avg_interval = recent.iter().sum::<u64>() as f64 / recent.len() as f64;
263
264 avg_interval < (1000.0 / self.burst_threshold)
266 }
267
268 #[must_use]
270 pub fn detected_period(&self) -> Option<u64> {
271 self.detected_period
272 }
273
274 #[must_use]
276 pub fn predict_next_access(&self, last_access: Instant) -> Option<Instant> {
277 self.detected_period
278 .map(|period| last_access + Duration::from_millis(period))
279 }
280}
281
282#[derive(Debug)]
288pub struct SpatialLocalityAnalyzer {
289 coordinates: VecDeque<(usize, usize, usize)>,
291 max_history: usize,
293 direction: Option<(i64, i64)>,
295 zoom_direction: Option<i64>,
297}
298
299impl SpatialLocalityAnalyzer {
300 #[must_use]
302 pub fn new(max_history: usize) -> Self {
303 Self {
304 coordinates: VecDeque::with_capacity(max_history),
305 max_history,
306 direction: None,
307 zoom_direction: None,
308 }
309 }
310
311 pub fn record_access(&mut self, x: usize, y: usize, z: usize) {
313 if let Some(&(prev_x, prev_y, prev_z)) = self.coordinates.back() {
315 let dx = x as i64 - prev_x as i64;
316 let dy = y as i64 - prev_y as i64;
317 let dz = z as i64 - prev_z as i64;
318
319 if let Some((curr_dx, curr_dy)) = self.direction {
321 self.direction = Some((
322 (curr_dx + dx) / 2, (curr_dy + dy) / 2,
324 ));
325 } else {
326 self.direction = Some((dx, dy));
327 }
328
329 self.zoom_direction = if dz != 0 { Some(dz) } else { None };
330 }
331
332 if self.coordinates.len() >= self.max_history {
333 self.coordinates.pop_front();
334 }
335 self.coordinates.push_back((x, y, z));
336 }
337
338 #[must_use]
340 pub fn predict_adjacent(&self, count: usize) -> Vec<(usize, usize, usize)> {
341 let Some(&(x, y, z)) = self.coordinates.back() else {
342 return Vec::new();
343 };
344
345 let mut predictions = Vec::with_capacity(count);
346
347 if let Some((dx, dy)) = self.direction {
349 let norm_dx = dx.signum();
351 let norm_dy = dy.signum();
352
353 if norm_dx != 0 {
355 let new_x = if norm_dx > 0 {
356 x + 1
357 } else {
358 x.saturating_sub(1)
359 };
360 predictions.push((new_x, y, z));
361 }
362 if norm_dy != 0 {
363 let new_y = if norm_dy > 0 {
364 y + 1
365 } else {
366 y.saturating_sub(1)
367 };
368 predictions.push((x, new_y, z));
369 }
370 if norm_dx != 0 && norm_dy != 0 {
372 let new_x = if norm_dx > 0 {
373 x + 1
374 } else {
375 x.saturating_sub(1)
376 };
377 let new_y = if norm_dy > 0 {
378 y + 1
379 } else {
380 y.saturating_sub(1)
381 };
382 predictions.push((new_x, new_y, z));
383 }
384 }
385
386 let spiral_offsets: [(i64, i64); 8] = [
388 (1, 0),
389 (0, 1),
390 (-1, 0),
391 (0, -1),
392 (1, 1),
393 (-1, 1),
394 (-1, -1),
395 (1, -1),
396 ];
397
398 for (ox, oy) in spiral_offsets {
399 if predictions.len() >= count {
400 break;
401 }
402 let new_x = (x as i64 + ox).max(0) as usize;
403 let new_y = (y as i64 + oy).max(0) as usize;
404 let coord = (new_x, new_y, z);
405 if !predictions.contains(&coord) {
406 predictions.push(coord);
407 }
408 }
409
410 if let Some(dz) = self.zoom_direction {
412 let new_z = (z as i64 + dz).max(0) as usize;
413 if predictions.len() < count {
414 predictions.push((x, y, new_z));
415 }
416 }
417
418 predictions.truncate(count);
419 predictions
420 }
421
422 #[must_use]
424 pub fn movement_direction(&self) -> Option<(i64, i64)> {
425 self.direction
426 }
427}
428
429pub struct PatternAnalyzer {
435 history: VecDeque<AccessRecord>,
437 max_history: usize,
439 current_pattern: AccessPattern,
441 temporal: TemporalPatternAnalyzer,
443 spatial: SpatialLocalityAnalyzer,
445 key_frequency: HashMap<String, u64>,
447 detected_stride: Option<i64>,
449}
450
451impl PatternAnalyzer {
452 #[must_use]
454 pub fn new(max_history: usize) -> Self {
455 Self {
456 history: VecDeque::with_capacity(max_history),
457 max_history,
458 current_pattern: AccessPattern::Unknown,
459 temporal: TemporalPatternAnalyzer::new(50),
460 spatial: SpatialLocalityAnalyzer::new(20),
461 key_frequency: HashMap::new(),
462 detected_stride: None,
463 }
464 }
465
466 pub fn record_access(&mut self, record: AccessRecord) {
468 if let Some(last) = self.history.back() {
470 let interval = record.timestamp.duration_since(last.timestamp);
471 self.temporal.record_interval(interval.as_millis() as u64);
472 }
473
474 if let Some((x, y, z)) = record.coordinates {
476 self.spatial.record_access(x, y, z);
477 }
478
479 *self.key_frequency.entry(record.key.clone()).or_insert(0) += 1;
481
482 if self.history.len() >= self.max_history {
484 self.history.pop_front();
485 }
486 self.history.push_back(record);
487
488 self.analyze_patterns();
490 }
491
492 fn analyze_patterns(&mut self) {
494 if self.history.len() < 3 {
495 self.current_pattern = AccessPattern::Unknown;
496 return;
497 }
498
499 if self.is_spatial() {
503 self.current_pattern = AccessPattern::Spatial;
504 return;
505 }
506
507 if let Some(stride) = self.detect_stride() {
509 self.detected_stride = Some(stride);
510 self.current_pattern = AccessPattern::Strided { stride };
511 return;
512 }
513
514 if self.is_sequential_forward() {
516 self.current_pattern = AccessPattern::SequentialForward;
517 return;
518 }
519
520 if self.is_sequential_backward() {
521 self.current_pattern = AccessPattern::SequentialBackward;
522 return;
523 }
524
525 if let Some(period) = self.temporal.detected_period() {
529 self.current_pattern = AccessPattern::TemporalPeriodic { period_ms: period };
530 return;
531 }
532
533 if self.temporal.is_burst() {
536 self.current_pattern = AccessPattern::Burst;
537 return;
538 }
539
540 self.current_pattern = AccessPattern::Random;
541 }
542
543 fn is_sequential_forward(&self) -> bool {
545 let recent: Vec<_> = self.history.iter().rev().take(5).collect();
546 if recent.len() < 3 {
547 return false;
548 }
549
550 let mut sequential_count = 0;
551 for window in recent.windows(2) {
552 if let (Some(&a), Some(&b)) = (window.first(), window.get(1))
553 && let (Some(na), Some(nb)) = (
554 extract_trailing_number(&a.key),
555 extract_trailing_number(&b.key),
556 )
557 {
558 if na == nb + 1 {
561 sequential_count += 1;
562 }
563 }
564 }
565
566 sequential_count >= (recent.len() - 1) / 2
567 }
568
569 fn is_sequential_backward(&self) -> bool {
571 let recent: Vec<_> = self.history.iter().rev().take(5).collect();
572 if recent.len() < 3 {
573 return false;
574 }
575
576 let mut backward_count = 0;
577 for window in recent.windows(2) {
578 if let (Some(&a), Some(&b)) = (window.first(), window.get(1))
579 && let (Some(na), Some(nb)) = (
580 extract_trailing_number(&a.key),
581 extract_trailing_number(&b.key),
582 )
583 {
584 if nb == na + 1 {
587 backward_count += 1;
588 }
589 }
590 }
591
592 backward_count >= (recent.len() - 1) / 2
593 }
594
595 fn detect_stride(&self) -> Option<i64> {
597 let recent: Vec<_> = self.history.iter().rev().take(6).collect();
598 if recent.len() < 4 {
599 return None;
600 }
601
602 let numbers: Vec<i64> = recent
604 .iter()
605 .filter_map(|r| extract_trailing_number(&r.key))
606 .collect();
607
608 if numbers.len() < 4 {
609 return None;
610 }
611
612 let diffs: Vec<i64> = numbers.windows(2).map(|w| w[0] - w[1]).collect();
614
615 if diffs.is_empty() {
616 return None;
617 }
618
619 let first_diff = diffs[0];
621 if first_diff.abs() <= 1 {
622 return None;
624 }
625
626 let consistent = diffs.iter().all(|&d| d == first_diff);
627 if consistent { Some(first_diff) } else { None }
628 }
629
630 fn is_spatial(&self) -> bool {
632 let coords_count = self
633 .history
634 .iter()
635 .rev()
636 .take(5)
637 .filter(|r| r.coordinates.is_some())
638 .count();
639
640 coords_count >= 3
641 }
642
643 #[must_use]
645 pub fn current_pattern(&self) -> AccessPattern {
646 self.current_pattern
647 }
648
649 #[must_use]
651 pub fn predict_next(&self, count: usize) -> Vec<PrefetchTarget> {
652 match self.current_pattern {
653 AccessPattern::SequentialForward => self.predict_sequential_forward(count),
654 AccessPattern::SequentialBackward => self.predict_sequential_backward(count),
655 AccessPattern::Strided { stride } => self.predict_strided(count, stride),
656 AccessPattern::Spatial => self.predict_spatial(count),
657 AccessPattern::Burst | AccessPattern::Random => self.predict_hot_spots(count),
658 AccessPattern::TemporalPeriodic { .. } => self.predict_recent(count),
659 AccessPattern::Unknown => Vec::new(),
660 }
661 }
662
663 fn predict_sequential_forward(&self, count: usize) -> Vec<PrefetchTarget> {
665 let Some(last) = self.history.back() else {
666 return Vec::new();
667 };
668
669 let mut predictions = Vec::new();
670 for i in 1..=count {
671 if let Some(next_key) = increment_key(&last.key, i as i64) {
672 let priority = if i == 1 {
673 PrefetchPriority::Critical
674 } else if i <= 3 {
675 PrefetchPriority::High
676 } else {
677 PrefetchPriority::Medium
678 };
679 let confidence = 1.0 - (i as f64 * 0.1).min(0.5);
680 predictions.push(PrefetchTarget::new(next_key, priority, confidence));
681 }
682 }
683 predictions
684 }
685
686 fn predict_sequential_backward(&self, count: usize) -> Vec<PrefetchTarget> {
688 let Some(last) = self.history.back() else {
689 return Vec::new();
690 };
691
692 let mut predictions = Vec::new();
693 for i in 1..=count {
694 if let Some(next_key) = increment_key(&last.key, -(i as i64)) {
695 let priority = if i == 1 {
696 PrefetchPriority::Critical
697 } else if i <= 3 {
698 PrefetchPriority::High
699 } else {
700 PrefetchPriority::Medium
701 };
702 let confidence = 1.0 - (i as f64 * 0.1).min(0.5);
703 predictions.push(PrefetchTarget::new(next_key, priority, confidence));
704 }
705 }
706 predictions
707 }
708
709 fn predict_strided(&self, count: usize, stride: i64) -> Vec<PrefetchTarget> {
711 let Some(last) = self.history.back() else {
712 return Vec::new();
713 };
714
715 let mut predictions = Vec::new();
716 for i in 1..=count {
717 if let Some(next_key) = increment_key(&last.key, stride * i as i64) {
718 let priority = if i == 1 {
719 PrefetchPriority::High
720 } else {
721 PrefetchPriority::Medium
722 };
723 let confidence = 0.8 - (i as f64 * 0.1).min(0.4);
724 predictions.push(PrefetchTarget::new(next_key, priority, confidence));
725 }
726 }
727 predictions
728 }
729
730 fn predict_spatial(&self, count: usize) -> Vec<PrefetchTarget> {
732 let coords = self.spatial.predict_adjacent(count);
733
734 coords
735 .into_iter()
736 .enumerate()
737 .map(|(i, (x, y, z))| {
738 let key = format!("tile_{x}_{y}_{z}");
739 let priority = if i == 0 {
740 PrefetchPriority::High
741 } else if i < 4 {
742 PrefetchPriority::Medium
743 } else {
744 PrefetchPriority::Low
745 };
746 let confidence = 0.9 - (i as f64 * 0.1).min(0.5);
747 PrefetchTarget::new(key, priority, confidence).with_coordinates(x, y, z)
748 })
749 .collect()
750 }
751
752 fn predict_hot_spots(&self, count: usize) -> Vec<PrefetchTarget> {
754 let mut freq_vec: Vec<_> = self.key_frequency.iter().collect();
755 freq_vec.sort_by(|a, b| b.1.cmp(a.1));
756
757 freq_vec
758 .into_iter()
759 .take(count)
760 .map(|(key, freq)| {
761 let priority = if *freq > 10 {
762 PrefetchPriority::High
763 } else if *freq > 5 {
764 PrefetchPriority::Medium
765 } else {
766 PrefetchPriority::Low
767 };
768 let confidence = (*freq as f64 / 20.0).min(0.8);
769 PrefetchTarget::new(key.clone(), priority, confidence)
770 })
771 .collect()
772 }
773
774 fn predict_recent(&self, count: usize) -> Vec<PrefetchTarget> {
776 self.history
777 .iter()
778 .rev()
779 .take(count)
780 .enumerate()
781 .map(|(i, record)| {
782 let priority = if i == 0 {
783 PrefetchPriority::Medium
784 } else {
785 PrefetchPriority::Low
786 };
787 PrefetchTarget::new(record.key.clone(), priority, 0.5)
788 })
789 .collect()
790 }
791}
792
793#[derive(Debug, Default)]
799pub struct BufferStats {
800 pub total_prefetches: AtomicU64,
802 pub successful_prefetches: AtomicU64,
804 pub wasted_prefetches: AtomicU64,
806 pub avg_latency_us: AtomicU64,
808 pub utilization_pct: AtomicU64,
810}
811
812impl BufferStats {
813 pub fn record_prefetch(&self, used: bool, latency_us: u64) {
815 self.total_prefetches.fetch_add(1, Ordering::Relaxed);
816 if used {
817 self.successful_prefetches.fetch_add(1, Ordering::Relaxed);
818 } else {
819 self.wasted_prefetches.fetch_add(1, Ordering::Relaxed);
820 }
821
822 let current = self.avg_latency_us.load(Ordering::Relaxed);
824 let new_avg = (current * 9 + latency_us) / 10;
825 self.avg_latency_us.store(new_avg, Ordering::Relaxed);
826 }
827
828 #[must_use]
830 pub fn hit_rate(&self) -> f64 {
831 let total = self.total_prefetches.load(Ordering::Relaxed);
832 if total == 0 {
833 return 0.0;
834 }
835 let successful = self.successful_prefetches.load(Ordering::Relaxed);
836 successful as f64 / total as f64
837 }
838}
839
840pub struct AdaptiveBufferSizer {
842 min_size: usize,
844 max_size: usize,
846 current_size: AtomicUsize,
848 stats: Arc<BufferStats>,
850 target_hit_rate: f64,
852 last_adjustment: RwLock<Instant>,
854 adjustment_interval: Duration,
856}
857
858impl AdaptiveBufferSizer {
859 #[must_use]
861 pub fn new(min_size: usize, max_size: usize) -> Self {
862 Self {
863 min_size,
864 max_size,
865 current_size: AtomicUsize::new(min_size),
866 stats: Arc::new(BufferStats::default()),
867 target_hit_rate: 0.7,
868 last_adjustment: RwLock::new(Instant::now()),
869 adjustment_interval: Duration::from_secs(10),
870 }
871 }
872
873 #[must_use]
875 pub fn current_size(&self) -> usize {
876 self.current_size.load(Ordering::Relaxed)
877 }
878
879 #[must_use]
881 pub fn stats(&self) -> Arc<BufferStats> {
882 Arc::clone(&self.stats)
883 }
884
885 pub async fn maybe_adjust(&self) {
887 let last = *self.last_adjustment.read().await;
888 if last.elapsed() < self.adjustment_interval {
889 return;
890 }
891
892 let hit_rate = self.stats.hit_rate();
893 let current = self.current_size.load(Ordering::Relaxed);
894
895 let new_size = if hit_rate < self.target_hit_rate - 0.1 {
896 (current as f64 * 1.5) as usize
898 } else if hit_rate > self.target_hit_rate + 0.1 {
899 (current as f64 * 0.9) as usize
901 } else {
902 current
903 };
904
905 let clamped = new_size.clamp(self.min_size, self.max_size);
906 self.current_size.store(clamped, Ordering::Relaxed);
907 *self.last_adjustment.write().await = Instant::now();
908
909 tracing::debug!(
910 "Adjusted prefetch buffer: {} -> {} bytes (hit_rate: {:.2})",
911 current,
912 clamped,
913 hit_rate
914 );
915 }
916}
917
918pub struct MemoryAwarePrefetcher {
924 max_memory: usize,
926 current_usage: AtomicUsize,
928 pressure_threshold: f64,
930}
931
932impl MemoryAwarePrefetcher {
933 #[must_use]
935 pub fn new(max_memory: usize) -> Self {
936 Self {
937 max_memory,
938 current_usage: AtomicUsize::new(0),
939 pressure_threshold: 0.8,
940 }
941 }
942
943 #[must_use]
945 pub fn can_prefetch(&self, size: usize) -> bool {
946 let current = self.current_usage.load(Ordering::Relaxed);
947 let usage_ratio = current as f64 / self.max_memory as f64;
948
949 if usage_ratio > self.pressure_threshold {
951 return false;
952 }
953
954 current + size <= self.max_memory
956 }
957
958 pub fn allocate(&self, size: usize) -> bool {
960 let current = self.current_usage.load(Ordering::Relaxed);
961 if current + size > self.max_memory {
962 return false;
963 }
964 self.current_usage.fetch_add(size, Ordering::Relaxed);
965 true
966 }
967
968 pub fn release(&self, size: usize) {
970 self.current_usage.fetch_sub(size, Ordering::Relaxed);
971 }
972
973 #[must_use]
975 pub fn current_usage(&self) -> usize {
976 self.current_usage.load(Ordering::Relaxed)
977 }
978
979 #[must_use]
981 pub fn pressure(&self) -> f64 {
982 let current = self.current_usage.load(Ordering::Relaxed);
983 current as f64 / self.max_memory as f64
984 }
985}
986
987pub struct BandwidthAwarePrefetcher {
989 max_bandwidth: usize,
991 bandwidth_used: AtomicUsize,
993 window_start: RwLock<Instant>,
995 window_duration: Duration,
997 samples: RwLock<VecDeque<(Instant, usize)>>,
999}
1000
1001impl BandwidthAwarePrefetcher {
1002 #[must_use]
1004 pub fn new(max_bandwidth: usize) -> Self {
1005 Self {
1006 max_bandwidth,
1007 bandwidth_used: AtomicUsize::new(0),
1008 window_start: RwLock::new(Instant::now()),
1009 window_duration: Duration::from_secs(1),
1010 samples: RwLock::new(VecDeque::with_capacity(100)),
1011 }
1012 }
1013
1014 pub async fn can_prefetch(&self, size: usize) -> bool {
1016 self.maybe_reset_window().await;
1017 let used = self.bandwidth_used.load(Ordering::Relaxed);
1018 used + size <= self.max_bandwidth
1019 }
1020
1021 pub async fn record_usage(&self, size: usize) {
1023 self.maybe_reset_window().await;
1024 self.bandwidth_used.fetch_add(size, Ordering::Relaxed);
1025
1026 let mut samples = self.samples.write().await;
1028 samples.push_back((Instant::now(), size));
1029 if samples.len() > 100 {
1030 samples.pop_front();
1031 }
1032 }
1033
1034 async fn maybe_reset_window(&self) {
1036 let start = *self.window_start.read().await;
1037 if start.elapsed() >= self.window_duration {
1038 self.bandwidth_used.store(0, Ordering::Relaxed);
1039 *self.window_start.write().await = Instant::now();
1040 }
1041 }
1042
1043 pub async fn estimated_bandwidth(&self) -> usize {
1045 let samples = self.samples.read().await;
1046 if samples.len() < 2 {
1047 return 0;
1048 }
1049
1050 let first = samples.front();
1051 let last = samples.back();
1052
1053 if let (Some((first_time, _)), Some((last_time, _))) = (first, last) {
1054 let duration = last_time.duration_since(*first_time);
1055 if duration.as_secs_f64() > 0.0 {
1056 let total_bytes: usize = samples.iter().map(|(_, s)| s).sum();
1057 return (total_bytes as f64 / duration.as_secs_f64()) as usize;
1058 }
1059 }
1060 0
1061 }
1062
1063 #[must_use]
1065 pub fn remaining_bandwidth(&self) -> usize {
1066 let used = self.bandwidth_used.load(Ordering::Relaxed);
1067 self.max_bandwidth.saturating_sub(used)
1068 }
1069}
1070
1071#[derive(Debug, Clone)]
1077pub struct PrefetchConfig {
1078 pub enabled: bool,
1080 pub prefetch_count: usize,
1082 pub max_concurrent: usize,
1084 pub bandwidth_limit: Option<usize>,
1086 pub memory_limit: usize,
1088 pub pattern_prediction: bool,
1090 pub adaptive_sizing: bool,
1092 pub min_confidence: f64,
1094}
1095
1096impl Default for PrefetchConfig {
1097 fn default() -> Self {
1098 Self {
1099 enabled: true,
1100 prefetch_count: 5,
1101 max_concurrent: 4,
1102 bandwidth_limit: None,
1103 memory_limit: 64 * 1024 * 1024, pattern_prediction: true,
1105 adaptive_sizing: true,
1106 min_confidence: 0.3,
1107 }
1108 }
1109}
1110
1111impl PrefetchConfig {
1112 #[must_use]
1114 pub fn new() -> Self {
1115 Self::default()
1116 }
1117
1118 #[must_use]
1120 pub fn with_enabled(mut self, enabled: bool) -> Self {
1121 self.enabled = enabled;
1122 self
1123 }
1124
1125 #[must_use]
1127 pub fn with_prefetch_count(mut self, count: usize) -> Self {
1128 self.prefetch_count = count;
1129 self
1130 }
1131
1132 #[must_use]
1134 pub fn with_max_concurrent(mut self, max: usize) -> Self {
1135 self.max_concurrent = max;
1136 self
1137 }
1138
1139 #[must_use]
1141 pub fn with_bandwidth_limit(mut self, limit: usize) -> Self {
1142 self.bandwidth_limit = Some(limit);
1143 self
1144 }
1145
1146 #[must_use]
1148 pub fn with_memory_limit(mut self, limit: usize) -> Self {
1149 self.memory_limit = limit;
1150 self
1151 }
1152
1153 #[must_use]
1155 pub fn with_adaptive_sizing(mut self, enabled: bool) -> Self {
1156 self.adaptive_sizing = enabled;
1157 self
1158 }
1159}
1160
1161#[cfg(feature = "async")]
1163pub struct PrefetchQueue {
1164 queues: RwLock<BTreeMap<PrefetchPriority, VecDeque<PrefetchTarget>>>,
1166 queued_keys: RwLock<std::collections::HashSet<String>>,
1168}
1169
1170#[cfg(feature = "async")]
1171impl PrefetchQueue {
1172 #[must_use]
1174 pub fn new() -> Self {
1175 Self {
1176 queues: RwLock::new(BTreeMap::new()),
1177 queued_keys: RwLock::new(std::collections::HashSet::new()),
1178 }
1179 }
1180
1181 pub async fn enqueue(&self, target: PrefetchTarget) {
1183 let mut keys = self.queued_keys.write().await;
1184 if keys.contains(&target.key) {
1185 return; }
1187 keys.insert(target.key.clone());
1188
1189 let mut queues = self.queues.write().await;
1190 queues
1191 .entry(target.priority)
1192 .or_insert_with(VecDeque::new)
1193 .push_back(target);
1194 }
1195
1196 pub async fn dequeue(&self) -> Option<PrefetchTarget> {
1198 let mut queues = self.queues.write().await;
1199
1200 for (_, queue) in queues.iter_mut().rev() {
1202 if let Some(target) = queue.pop_front() {
1203 let mut keys = self.queued_keys.write().await;
1204 keys.remove(&target.key);
1205 return Some(target);
1206 }
1207 }
1208 None
1209 }
1210
1211 pub async fn len(&self) -> usize {
1213 self.queued_keys.read().await.len()
1214 }
1215
1216 pub async fn is_empty(&self) -> bool {
1218 self.queued_keys.read().await.is_empty()
1219 }
1220}
1221
1222#[cfg(feature = "async")]
1223impl Default for PrefetchQueue {
1224 fn default() -> Self {
1225 Self::new()
1226 }
1227}
1228
1229#[cfg(feature = "async")]
1231pub struct PrefetchManager {
1232 config: PrefetchConfig,
1234 analyzer: Arc<RwLock<PatternAnalyzer>>,
1236 queue: Arc<PrefetchQueue>,
1238 memory: Arc<MemoryAwarePrefetcher>,
1240 bandwidth: Arc<BandwidthAwarePrefetcher>,
1242 buffer_sizer: Option<Arc<AdaptiveBufferSizer>>,
1244}
1245
1246#[cfg(feature = "async")]
1247impl PrefetchManager {
1248 #[must_use]
1250 pub fn new(config: PrefetchConfig) -> Self {
1251 let memory = Arc::new(MemoryAwarePrefetcher::new(config.memory_limit));
1252
1253 let bandwidth = Arc::new(BandwidthAwarePrefetcher::new(
1254 config.bandwidth_limit.unwrap_or(usize::MAX),
1255 ));
1256
1257 let buffer_sizer = if config.adaptive_sizing {
1258 Some(Arc::new(AdaptiveBufferSizer::new(
1259 config.memory_limit / 4,
1260 config.memory_limit,
1261 )))
1262 } else {
1263 None
1264 };
1265
1266 Self {
1267 config,
1268 analyzer: Arc::new(RwLock::new(PatternAnalyzer::new(100))),
1269 queue: Arc::new(PrefetchQueue::new()),
1270 memory,
1271 bandwidth,
1272 buffer_sizer,
1273 }
1274 }
1275
1276 pub async fn record_access(&self, record: AccessRecord) -> Vec<PrefetchTarget> {
1278 if !self.config.enabled {
1279 return Vec::new();
1280 }
1281
1282 let mut analyzer = self.analyzer.write().await;
1283 analyzer.record_access(record);
1284
1285 if self.config.pattern_prediction {
1286 let mut predictions = analyzer.predict_next(self.config.prefetch_count);
1287
1288 predictions.retain(|t| t.confidence >= self.config.min_confidence);
1290
1291 for target in &predictions {
1293 self.queue.enqueue(target.clone()).await;
1294 }
1295
1296 predictions
1297 } else {
1298 Vec::new()
1299 }
1300 }
1301
1302 pub async fn can_prefetch(&self, size: usize) -> bool {
1304 if !self.memory.can_prefetch(size) {
1306 return false;
1307 }
1308
1309 if !self.bandwidth.can_prefetch(size).await {
1311 return false;
1312 }
1313
1314 true
1315 }
1316
1317 pub async fn record_prefetch(&self, size: usize, used: bool, latency: Duration) {
1319 self.bandwidth.record_usage(size).await;
1320
1321 if let Some(ref sizer) = self.buffer_sizer {
1322 sizer
1323 .stats()
1324 .record_prefetch(used, latency.as_micros() as u64);
1325 sizer.maybe_adjust().await;
1326 }
1327 }
1328
1329 pub fn allocate_memory(&self, size: usize) -> bool {
1331 self.memory.allocate(size)
1332 }
1333
1334 pub fn release_memory(&self, size: usize) {
1336 self.memory.release(size);
1337 }
1338
1339 pub async fn next_target(&self) -> Option<PrefetchTarget> {
1341 self.queue.dequeue().await
1342 }
1343
1344 pub async fn current_pattern(&self) -> AccessPattern {
1346 self.analyzer.read().await.current_pattern()
1347 }
1348
1349 pub async fn queue_len(&self) -> usize {
1351 self.queue.len().await
1352 }
1353
1354 #[must_use]
1356 pub fn memory_pressure(&self) -> f64 {
1357 self.memory.pressure()
1358 }
1359
1360 #[must_use]
1362 pub fn remaining_bandwidth(&self) -> usize {
1363 self.bandwidth.remaining_bandwidth()
1364 }
1365
1366 #[must_use]
1368 pub fn hit_rate(&self) -> f64 {
1369 self.buffer_sizer
1370 .as_ref()
1371 .map_or(0.0, |s| s.stats().hit_rate())
1372 }
1373}
1374
1375fn extract_trailing_number(key: &str) -> Option<i64> {
1381 let parts: Vec<&str> = key
1382 .split(|c: char| !c.is_ascii_digit() && c != '-')
1383 .collect();
1384 parts
1385 .iter()
1386 .rev()
1387 .find(|s| !s.is_empty())
1388 .and_then(|s| s.parse().ok())
1389}
1390
1391fn increment_key(key: &str, offset: i64) -> Option<String> {
1393 let parts: Vec<&str> = key.split('_').collect();
1394
1395 for i in (0..parts.len()).rev() {
1396 if let Ok(num) = parts[i].parse::<i64>() {
1397 let new_num = num + offset;
1398 if new_num < 0 {
1399 return None;
1400 }
1401 let mut new_parts: Vec<String> = parts.iter().map(|s| (*s).to_string()).collect();
1402 new_parts[i] = new_num.to_string();
1403 return Some(new_parts.join("_"));
1404 }
1405 }
1406 None
1407}
1408
1409#[cfg(test)]
1414mod tests {
1415 use super::*;
1416
1417 #[test]
1418 fn test_extract_trailing_number() {
1419 assert_eq!(extract_trailing_number("file_5"), Some(5));
1420 assert_eq!(extract_trailing_number("tile_10_20_3"), Some(3));
1421 assert_eq!(extract_trailing_number("data"), None);
1422 }
1423
1424 #[test]
1425 fn test_increment_key() {
1426 assert_eq!(increment_key("file_5", 1), Some("file_6".to_string()));
1427 assert_eq!(increment_key("file_5", -1), Some("file_4".to_string()));
1428 assert_eq!(
1429 increment_key("tile_10_20", 1),
1430 Some("tile_10_21".to_string())
1431 );
1432 assert_eq!(increment_key("file_0", -1), None);
1433 }
1434
1435 #[test]
1436 fn test_temporal_pattern_analyzer() {
1437 let mut analyzer = TemporalPatternAnalyzer::new(20);
1438
1439 for _ in 0..10 {
1441 analyzer.record_interval(1000); }
1443
1444 assert!(analyzer.detected_period().is_some());
1445 let period = analyzer.detected_period();
1446 assert!(period.is_some_and(|p| (p as i64 - 1000).abs() < 100));
1447 }
1448
1449 #[test]
1450 fn test_temporal_burst_detection() {
1451 let mut analyzer = TemporalPatternAnalyzer::new(20);
1452
1453 for _ in 0..10 {
1455 analyzer.record_interval(50); }
1457
1458 assert!(analyzer.is_burst());
1459 }
1460
1461 #[test]
1462 fn test_spatial_locality_analyzer() {
1463 let mut analyzer = SpatialLocalityAnalyzer::new(10);
1464
1465 analyzer.record_access(0, 0, 0);
1467 analyzer.record_access(1, 0, 0);
1468 analyzer.record_access(2, 0, 0);
1469
1470 let predictions = analyzer.predict_adjacent(4);
1471 assert!(!predictions.is_empty());
1472
1473 assert!(predictions.iter().any(|&(x, _, _)| x == 3));
1475 }
1476
1477 #[test]
1478 fn test_pattern_analyzer_sequential() {
1479 let mut analyzer = PatternAnalyzer::new(10);
1480
1481 analyzer.record_access(AccessRecord::new("file_0".to_string()));
1482 analyzer.record_access(AccessRecord::new("file_1".to_string()));
1483 analyzer.record_access(AccessRecord::new("file_2".to_string()));
1484 analyzer.record_access(AccessRecord::new("file_3".to_string()));
1485
1486 assert!(matches!(
1487 analyzer.current_pattern(),
1488 AccessPattern::SequentialForward | AccessPattern::SequentialBackward
1489 ));
1490
1491 let predictions = analyzer.predict_next(3);
1492 assert!(!predictions.is_empty());
1493 }
1494
1495 #[test]
1496 fn test_pattern_analyzer_spatial() {
1497 let mut analyzer = PatternAnalyzer::new(10);
1498
1499 analyzer.record_access(AccessRecord::with_coordinates(
1500 "tile_0_0_0".to_string(),
1501 0,
1502 0,
1503 0,
1504 ));
1505 analyzer.record_access(AccessRecord::with_coordinates(
1506 "tile_1_0_0".to_string(),
1507 1,
1508 0,
1509 0,
1510 ));
1511 analyzer.record_access(AccessRecord::with_coordinates(
1512 "tile_2_0_0".to_string(),
1513 2,
1514 0,
1515 0,
1516 ));
1517 analyzer.record_access(AccessRecord::with_coordinates(
1518 "tile_3_0_0".to_string(),
1519 3,
1520 0,
1521 0,
1522 ));
1523
1524 assert_eq!(analyzer.current_pattern(), AccessPattern::Spatial);
1525
1526 let predictions = analyzer.predict_next(4);
1527 assert!(!predictions.is_empty());
1528 }
1529
1530 #[test]
1531 fn test_prefetch_target_priority_ordering() {
1532 let targets = [
1533 PrefetchTarget::new("low".to_string(), PrefetchPriority::Low, 0.5),
1534 PrefetchTarget::new("critical".to_string(), PrefetchPriority::Critical, 0.9),
1535 PrefetchTarget::new("medium".to_string(), PrefetchPriority::Medium, 0.7),
1536 ];
1537
1538 let mut sorted: Vec<_> = targets.iter().map(|t| t.priority).collect();
1539 sorted.sort();
1540
1541 assert_eq!(sorted[0], PrefetchPriority::Low);
1542 assert_eq!(sorted[1], PrefetchPriority::Medium);
1543 assert_eq!(sorted[2], PrefetchPriority::Critical);
1544 }
1545
1546 #[test]
1547 fn test_memory_aware_prefetcher() {
1548 let prefetcher = MemoryAwarePrefetcher::new(1000);
1549
1550 assert!(prefetcher.can_prefetch(500));
1551 assert!(prefetcher.allocate(500));
1552 assert_eq!(prefetcher.current_usage(), 500);
1553
1554 assert!(prefetcher.can_prefetch(400));
1555 assert!(!prefetcher.can_prefetch(600)); prefetcher.release(200);
1558 assert_eq!(prefetcher.current_usage(), 300);
1559 }
1560
1561 #[test]
1562 fn test_buffer_stats() {
1563 let stats = BufferStats::default();
1564
1565 stats.record_prefetch(true, 100);
1566 stats.record_prefetch(true, 200);
1567 stats.record_prefetch(false, 150);
1568
1569 assert_eq!(stats.total_prefetches.load(Ordering::Relaxed), 3);
1570 assert_eq!(stats.successful_prefetches.load(Ordering::Relaxed), 2);
1571 assert_eq!(stats.wasted_prefetches.load(Ordering::Relaxed), 1);
1572
1573 let hit_rate = stats.hit_rate();
1574 assert!((hit_rate - 0.666).abs() < 0.01);
1575 }
1576
1577 #[test]
1578 fn test_prefetch_config_builder() {
1579 let config = PrefetchConfig::new()
1580 .with_enabled(true)
1581 .with_prefetch_count(10)
1582 .with_max_concurrent(8)
1583 .with_bandwidth_limit(1_000_000)
1584 .with_memory_limit(128 * 1024 * 1024)
1585 .with_adaptive_sizing(true);
1586
1587 assert!(config.enabled);
1588 assert_eq!(config.prefetch_count, 10);
1589 assert_eq!(config.max_concurrent, 8);
1590 assert_eq!(config.bandwidth_limit, Some(1_000_000));
1591 assert_eq!(config.memory_limit, 128 * 1024 * 1024);
1592 assert!(config.adaptive_sizing);
1593 }
1594
1595 #[cfg(feature = "async")]
1596 #[tokio::test]
1597 async fn test_prefetch_queue() {
1598 let queue = PrefetchQueue::new();
1599
1600 queue
1601 .enqueue(PrefetchTarget::new(
1602 "low".to_string(),
1603 PrefetchPriority::Low,
1604 0.5,
1605 ))
1606 .await;
1607 queue
1608 .enqueue(PrefetchTarget::new(
1609 "high".to_string(),
1610 PrefetchPriority::High,
1611 0.9,
1612 ))
1613 .await;
1614 queue
1615 .enqueue(PrefetchTarget::new(
1616 "medium".to_string(),
1617 PrefetchPriority::Medium,
1618 0.7,
1619 ))
1620 .await;
1621
1622 assert_eq!(queue.len().await, 3);
1623
1624 let first = queue.dequeue().await;
1626 assert!(first.is_some());
1627 assert_eq!(first.map(|t| t.key), Some("high".to_string()));
1628
1629 let second = queue.dequeue().await;
1630 assert!(second.is_some());
1631 assert_eq!(second.map(|t| t.key), Some("medium".to_string()));
1632 }
1633
1634 #[cfg(feature = "async")]
1635 #[tokio::test]
1636 async fn test_prefetch_queue_deduplication() {
1637 let queue = PrefetchQueue::new();
1638
1639 queue
1640 .enqueue(PrefetchTarget::new(
1641 "key1".to_string(),
1642 PrefetchPriority::High,
1643 0.9,
1644 ))
1645 .await;
1646 queue
1647 .enqueue(PrefetchTarget::new(
1648 "key1".to_string(),
1649 PrefetchPriority::Critical,
1650 0.95,
1651 ))
1652 .await;
1653
1654 assert_eq!(queue.len().await, 1);
1656 }
1657
1658 #[cfg(feature = "async")]
1659 #[tokio::test]
1660 async fn test_prefetch_manager() {
1661 let config = PrefetchConfig::new()
1662 .with_prefetch_count(3)
1663 .with_memory_limit(1024 * 1024);
1664 let manager = PrefetchManager::new(config);
1665
1666 manager
1668 .record_access(AccessRecord::new("file_0".to_string()))
1669 .await;
1670 manager
1671 .record_access(AccessRecord::new("file_1".to_string()))
1672 .await;
1673 manager
1674 .record_access(AccessRecord::new("file_2".to_string()))
1675 .await;
1676
1677 let predictions = manager
1678 .record_access(AccessRecord::new("file_3".to_string()))
1679 .await;
1680
1681 assert!(!predictions.is_empty());
1683 }
1684
1685 #[cfg(feature = "async")]
1686 #[tokio::test]
1687 async fn test_prefetch_manager_memory_pressure() {
1688 let config = PrefetchConfig::new().with_memory_limit(1000);
1689 let manager = PrefetchManager::new(config);
1690
1691 assert!(manager.allocate_memory(800));
1693 assert!(manager.memory_pressure() > 0.7);
1694
1695 assert!(!manager.can_prefetch(300).await);
1697
1698 manager.release_memory(500);
1700 assert!(manager.can_prefetch(300).await);
1701 }
1702
1703 #[cfg(feature = "async")]
1704 #[tokio::test]
1705 async fn test_bandwidth_aware_prefetcher() {
1706 let prefetcher = BandwidthAwarePrefetcher::new(1000);
1707
1708 assert!(prefetcher.can_prefetch(500).await);
1709 prefetcher.record_usage(500).await;
1710
1711 assert!(prefetcher.can_prefetch(400).await);
1712 prefetcher.record_usage(400).await;
1713
1714 assert!(!prefetcher.can_prefetch(200).await);
1716 assert!(prefetcher.remaining_bandwidth() < 200);
1717 }
1718}