scirs2_core/memory_efficient/
resource_aware.rs

1//! Resource-aware prefetching system that adapts to system load.
2//!
3//! This module provides a prefetching system that monitors system resources (CPU, memory, IO)
4//! and dynamically adjusts its prefetching strategy to avoid overloading the system.
5//! This helps ensure that the prefetching system improves rather than hinders performance.
6
7use std::collections::VecDeque;
8use std::sync::{Arc, Mutex};
9use std::time::{Duration, Instant};
10
11use super::prefetch::{PrefetchConfig, PrefetchStats};
12
13/// Default sampling interval for resource monitoring
14const DEFAULT_SAMPLING_INTERVAL: Duration = Duration::from_millis(500);
15
16/// Default memory pressure threshold (percentage of available memory)
17const DEFAULT_MEMORY_PRESSURE_THRESHOLD: f64 = 0.85;
18
19/// Default CPU load threshold
20const DEFAULT_CPU_LOAD_THRESHOLD: f64 = 0.85;
21
22/// Default IO pressure threshold
23const DEFAULT_IO_PRESSURE_THRESHOLD: f64 = 0.85;
24
25/// Minimum interval between strategy adjustments
26const MIN_ADJUSTMENT_INTERVAL: Duration = Duration::from_secs(1);
27
28/// Maximum interval for resource snapshots
29const MAX_SNAPSHOT_HISTORY: usize = 20;
30
31/// Types of system resources that can be monitored.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum ResourceType {
34    /// CPU utilization
35    CPU,
36
37    /// Memory usage
38    Memory,
39
40    /// IO operations
41    IO,
42
43    /// Combined resource pressure
44    Combined,
45}
46
47/// Snapshot of system resource usage at a point in time.
48#[derive(Debug, Clone)]
49pub struct ResourceSnapshot {
50    /// Timestamp of the snapshot
51    pub timestamp: Instant,
52
53    /// CPU usage (0.0 to 1.0)
54    pub cpu_usage: f64,
55
56    /// Memory usage (bytes)
57    pub memory_usage: u64,
58
59    /// Available memory (bytes)
60    pub memory_available: u64,
61
62    /// IO operations per second
63    pub io_ops_per_sec: u64,
64
65    /// IO bytes per second
66    pub io_bytes_per_sec: u64,
67}
68
69impl ResourceSnapshot {
70    /// Calculate memory pressure (0.0 to 1.0).
71    pub fn memory_pressure(&self) -> f64 {
72        if self.memory_available == 0 {
73            0.0 // Avoid division by zero
74        } else {
75            self.memory_usage as f64 / (self.memory_usage + self.memory_available) as f64
76        }
77    }
78
79    /// Calculate combined resource pressure (0.0 to 1.0).
80    pub fn combined_pressure(&self) -> f64 {
81        // Weight factors for different resources
82        const CPU_WEIGHT: f64 = 0.4;
83        const MEMORY_WEIGHT: f64 = 0.4;
84        const IO_WEIGHT: f64 = 0.2;
85
86        // Normalized IO pressure (estimate)
87        let io_pressure = if self.io_bytes_per_sec > 100_000_000 {
88            // Over 100MB/s is high IO
89            0.9
90        } else if self.io_bytes_per_sec > 50_000_000 {
91            // 50-100MB/s is medium IO
92            0.7
93        } else if self.io_bytes_per_sec > 10_000_000 {
94            // 10-50MB/s is moderate IO
95            0.5
96        } else {
97            // Under 10MB/s is low IO
98            0.2
99        };
100
101        // Combined pressure
102        CPU_WEIGHT * self.cpu_usage
103            + MEMORY_WEIGHT * self.memory_pressure()
104            + IO_WEIGHT * io_pressure
105    }
106}
107
108/// Configuration for resource-aware prefetching.
109#[derive(Debug, Clone)]
110pub struct ResourceAwareConfig {
111    /// How often to sample system resources
112    pub sampling_interval: Duration,
113
114    /// Threshold for memory pressure (0.0 to 1.0)
115    pub memory_pressure_threshold: f64,
116
117    /// Threshold for CPU load (0.0 to 1.0)
118    pub cpu_load_threshold: f64,
119
120    /// Threshold for IO pressure (0.0 to 1.0)
121    pub io_pressure_threshold: f64,
122
123    /// Minimum time between strategy adjustments
124    pub adjustment_interval: Duration,
125
126    /// Whether to automatically adjust prefetching based on resources
127    pub auto_adjust: bool,
128
129    /// Whether to disable prefetching when system is under very high load
130    pub disable_under_pressure: bool,
131
132    /// Minimum prefetch count (even under load)
133    pub min_prefetch_count: usize,
134
135    /// Maximum prefetch count (when resources are abundant)
136    pub max_prefetch_count: usize,
137}
138
139impl Default for ResourceAwareConfig {
140    fn default() -> Self {
141        Self {
142            sampling_interval: DEFAULT_SAMPLING_INTERVAL,
143            memory_pressure_threshold: DEFAULT_MEMORY_PRESSURE_THRESHOLD,
144            cpu_load_threshold: DEFAULT_CPU_LOAD_THRESHOLD,
145            io_pressure_threshold: DEFAULT_IO_PRESSURE_THRESHOLD,
146            adjustment_interval: MIN_ADJUSTMENT_INTERVAL,
147            auto_adjust: true,
148            disable_under_pressure: true,
149            min_prefetch_count: 1,
150            max_prefetch_count: 8,
151        }
152    }
153}
154
155/// Builder for resource-aware configuration.
156#[derive(Debug, Clone, Default)]
157pub struct ResourceAwareConfigBuilder {
158    config: ResourceAwareConfig,
159}
160
161impl ResourceAwareConfigBuilder {
162    /// Create a new resource-aware config builder with default settings.
163    pub fn new() -> Self {
164        Self::default()
165    }
166
167    /// Set the sampling interval.
168    pub const fn with_sampling_interval(mut self, interval: Duration) -> Self {
169        self.config.sampling_interval = interval;
170        self
171    }
172
173    /// Set the memory pressure threshold.
174    pub fn with_memory_pressure_threshold(mut self, threshold: f64) -> Self {
175        self.config.memory_pressure_threshold = threshold.clamp(0.0, 1.0);
176        self
177    }
178
179    /// Set the CPU load threshold.
180    pub fn with_cpu_load_threshold(mut self, threshold: f64) -> Self {
181        self.config.cpu_load_threshold = threshold.clamp(0.0, 1.0);
182        self
183    }
184
185    /// Set the IO pressure threshold.
186    pub fn with_io_pressure_threshold(mut self, threshold: f64) -> Self {
187        self.config.io_pressure_threshold = threshold.clamp(0.0, 1.0);
188        self
189    }
190
191    /// Set the adjustment interval.
192    pub fn with_adjustment_interval(mut self, interval: Duration) -> Self {
193        self.config.adjustment_interval = std::cmp::max(interval, MIN_ADJUSTMENT_INTERVAL);
194        self
195    }
196
197    /// Enable or disable automatic adjustment.
198    pub fn with_auto_adjust(mut self, autoadjust: bool) -> Self {
199        self.config.auto_adjust = autoadjust;
200        self
201    }
202
203    /// Enable or disable disabling prefetching under high load.
204    pub const fn with_disable_under_pressure(mut self, disable: bool) -> Self {
205        self.config.disable_under_pressure = disable;
206        self
207    }
208
209    /// Set the minimum prefetch count.
210    pub const fn with_min_prefetch_count(mut self, count: usize) -> Self {
211        self.config.min_prefetch_count = count;
212        self
213    }
214
215    /// Set the maximum prefetch count.
216    pub const fn with_max_prefetch_count(mut self, count: usize) -> Self {
217        self.config.max_prefetch_count = count;
218        self
219    }
220
221    /// Build the configuration.
222    pub fn build(self) -> ResourceAwareConfig {
223        self.config
224    }
225}
226
227/// Resource monitor for tracking system load.
228pub struct ResourceMonitor {
229    /// Configuration for resource monitoring
230    config: ResourceAwareConfig,
231
232    /// History of resource snapshots
233    snapshots: VecDeque<ResourceSnapshot>,
234
235    /// Last time resources were sampled
236    last_sample: Instant,
237
238    /// Last time prefetching strategy was adjusted
239    last_adjustment: Instant,
240
241    /// Current pressure status (true if under pressure)
242    under_pressure: bool,
243
244    /// System info provider
245    sys_info: Box<dyn SystemInfo + Send + Sync>,
246}
247
248impl ResourceMonitor {
249    /// Create a new resource monitor with the given configuration.
250    pub fn new(config: ResourceAwareConfig) -> Self {
251        Self {
252            config,
253            snapshots: VecDeque::with_capacity(MAX_SNAPSHOT_HISTORY),
254            last_sample: Instant::now(),
255            last_adjustment: Instant::now(),
256            under_pressure: false,
257            sys_info: Box::new(DefaultSystemInfo),
258        }
259    }
260
261    /// Take a snapshot of current system resources.
262    pub fn take_snapshot(&mut self) -> ResourceSnapshot {
263        // Get CPU usage
264        let cpu_usage = self.sys_info.get_cpu_usage();
265
266        // Get memory usage
267        let (memory_usage, memory_available) = self.sys_info.get_memoryinfo();
268
269        // Get IO stats
270        let (io_ops_per_sec, io_bytes_per_sec) = self.sys_info.get_io_stats();
271
272        // Create snapshot
273        let snapshot = ResourceSnapshot {
274            timestamp: Instant::now(),
275            cpu_usage,
276            memory_usage,
277            memory_available,
278            io_ops_per_sec,
279            io_bytes_per_sec,
280        };
281
282        // Update history
283        self.snapshots.push_back(snapshot.clone());
284        while self.snapshots.len() > MAX_SNAPSHOT_HISTORY {
285            self.snapshots.pop_front();
286        }
287
288        // Update last sample time
289        self.last_sample = Instant::now();
290
291        snapshot
292    }
293
294    /// Check if it's time to take a new snapshot.
295    pub fn should_take_snapshot(&self) -> bool {
296        self.last_sample.elapsed() >= self.config.sampling_interval
297    }
298
299    /// Check if the system is under resource pressure.
300    pub fn is_under_pressure(&mut self) -> bool {
301        // Take a snapshot if needed
302        if self.should_take_snapshot() {
303            self.take_snapshot();
304        }
305
306        // Check if we have snapshots
307        if self.snapshots.is_empty() {
308            return false;
309        }
310
311        // Get the latest snapshot
312        let latest = self.snapshots.back().expect("Operation failed");
313
314        // Check each resource
315        let cpu_pressure = latest.cpu_usage > self.config.cpu_load_threshold;
316        let memory_pressure = latest.memory_pressure() > self.config.memory_pressure_threshold;
317
318        // Calculate IO pressure
319        let io_pressure = if latest.io_bytes_per_sec > 100_000_000 {
320            // Over 100MB/s is considered high IO
321            true
322        } else {
323            false
324        };
325
326        // Combined pressure
327        self.under_pressure = cpu_pressure || memory_pressure || io_pressure;
328
329        self.under_pressure
330    }
331
332    /// Get the optimal prefetch count based on current resources.
333    pub fn count(&mut self, base_prefetchcount: usize) -> usize {
334        if !self.config.auto_adjust {
335            return base_prefetchcount;
336        }
337
338        // Take a snapshot if needed
339        if self.should_take_snapshot() {
340            self.take_snapshot();
341        }
342
343        // Check if we have snapshots
344        if self.snapshots.is_empty() {
345            return base_prefetchcount;
346        }
347
348        // Get the latest snapshot
349        let latest = self.snapshots.back().expect("Operation failed");
350
351        // Calculate combined pressure
352        let pressure = latest.combined_pressure();
353
354        // Adjust prefetch _count based on pressure
355        if pressure > 0.90 && self.config.disable_under_pressure {
356            // Very high pressure, drastically reduce or disable prefetching
357            self.config.min_prefetch_count
358        } else if pressure > 0.75 {
359            // High pressure, reduce prefetching
360            std::cmp::max(
361                self.config.min_prefetch_count,
362                (base_prefetchcount as f64 * 0.5).round() as usize,
363            )
364        } else if pressure > 0.6 {
365            // Moderate pressure, slightly reduce prefetching
366            std::cmp::max(
367                self.config.min_prefetch_count,
368                (base_prefetchcount as f64 * 0.75).round() as usize,
369            )
370        } else if pressure < 0.3 {
371            // Low pressure, can increase prefetching
372            std::cmp::min(
373                self.config.max_prefetch_count,
374                (base_prefetchcount as f64 * 1.5).round() as usize,
375            )
376        } else {
377            // Normal pressure, use base _count
378            base_prefetchcount
379        }
380    }
381
382    /// Adjust prefetching configuration based on resource pressure.
383    pub fn adjust_prefetch_config(&mut self, config: &mut PrefetchConfig) -> bool {
384        if !self.config.auto_adjust
385            || self.last_adjustment.elapsed() < self.config.adjustment_interval
386        {
387            return false;
388        }
389
390        // Get optimal prefetch count
391        let optimal_prefetch_count = self.get_optimal_prefetch_count(config.prefetch_count);
392
393        // Check if we need to adjust
394        if optimal_prefetch_count != config.prefetch_count {
395            config.prefetch_count = optimal_prefetch_count;
396            self.last_adjustment = Instant::now();
397            return true;
398        }
399
400        false
401    }
402
403    /// Get the optimal prefetch count based on current resources.
404    pub fn get_optimal_prefetch_count(&mut self, base_prefetchcount: usize) -> usize {
405        self.count(base_prefetchcount)
406    }
407
408    /// Get the latest resource snapshot.
409    pub fn get_latest_snapshot(&self) -> Option<ResourceSnapshot> {
410        self.snapshots.back().cloned()
411    }
412
413    /// Get a summary of recent resource usage.
414    pub fn get_resource_summary(&self) -> ResourceSummary {
415        if self.snapshots.is_empty() {
416            return ResourceSummary::default();
417        }
418
419        // Calculate averages
420        let mut cpu_sum = 0.0;
421        let mut memory_pressure_sum = 0.0;
422        let mut io_bytes_sum = 0;
423
424        for snapshot in &self.snapshots {
425            cpu_sum += snapshot.cpu_usage;
426            memory_pressure_sum += snapshot.memory_pressure();
427            io_bytes_sum += snapshot.io_bytes_per_sec;
428        }
429
430        let count = self.snapshots.len();
431        let avg_cpu = cpu_sum / count as f64;
432        let avg_memory_pressure = memory_pressure_sum / count as f64;
433        let avg_io_bytes = io_bytes_sum / count as u64;
434
435        // Calculate trends (compare recent with older snapshots)
436        let trend_duration = if count >= 2 {
437            let oldest = &self.snapshots[0];
438            let newest = self.snapshots.back().expect("Operation failed");
439
440            newest.timestamp.duration_since(oldest.timestamp)
441        } else {
442            Duration::from_secs(0)
443        };
444
445        ResourceSummary {
446            avg_cpu_usage: avg_cpu,
447            avg_memory_pressure,
448            avg_io_bytes_per_sec: avg_io_bytes,
449            combined_pressure: self
450                .snapshots
451                .back()
452                .expect("Operation failed")
453                .combined_pressure(),
454            snapshot_count: count,
455            duration: trend_duration,
456            under_pressure: self.under_pressure,
457        }
458    }
459}
460
461/// Summary of resource usage over time.
462#[derive(Debug, Clone)]
463pub struct ResourceSummary {
464    /// Average CPU usage (0.0 to 1.0)
465    pub avg_cpu_usage: f64,
466
467    /// Average memory pressure (0.0 to 1.0)
468    pub avg_memory_pressure: f64,
469
470    /// Average IO bytes per second
471    pub avg_io_bytes_per_sec: u64,
472
473    /// Combined resource pressure (from latest snapshot)
474    pub combined_pressure: f64,
475
476    /// Number of snapshots used for the summary
477    pub snapshot_count: usize,
478
479    /// Duration covered by the snapshots
480    pub duration: Duration,
481
482    /// Whether the system is currently under pressure
483    pub under_pressure: bool,
484}
485
486impl Default for ResourceSummary {
487    fn default() -> Self {
488        Self {
489            avg_cpu_usage: 0.0,
490            avg_memory_pressure: 0.0,
491            avg_io_bytes_per_sec: 0,
492            combined_pressure: 0.0,
493            snapshot_count: 0,
494            duration: Duration::from_secs(0),
495            under_pressure: false,
496        }
497    }
498}
499
500/// Interface for getting system information.
501pub trait SystemInfo {
502    /// Get CPU usage (0.0 to 1.0).
503    fn get_cpu_usage(&self) -> f64;
504
505    /// Get memory information (usage, available).
506    fn get_memoryinfo(&self) -> (u64, u64);
507
508    /// Get IO statistics (ops/sec, bytes/sec).
509    fn get_io_stats(&self) -> (u64, u64);
510}
511
512/// Default implementation of system info using sysinfo crate.
513pub struct DefaultSystemInfo;
514
515impl SystemInfo for DefaultSystemInfo {
516    fn get_cpu_usage(&self) -> f64 {
517        // Try to get CPU usage if sysinfo is available
518        #[cfg(feature = "sysinfo")]
519        {
520            use sysinfo::System;
521            let mut system = System::new_all();
522            system.refresh_cpu_all();
523
524            // Calculate average CPU usage across all cores
525            let cpu_usage: f64 = system
526                .cpus()
527                .iter()
528                .map(|cpu| cpu.cpu_usage() as f64 / 100.0)
529                .sum();
530            cpu_usage / system.cpus().len() as f64
531        }
532
533        // Fallback to a reasonable estimate
534        #[cfg(not(feature = "sysinfo"))]
535        {
536            // Without sysinfo, use getloadavg() if on Unix-like
537            #[cfg(all(
538                target_family = "unix",
539                feature = "memory_compression",
540                feature = "cross_platform"
541            ))]
542            {
543                let mut loadavg = [0.0, 0.0, 0.0];
544                if unsafe { libc::getloadavg(loadavg.as_mut_ptr(), 3) } == 3 {
545                    // Normalize load average to 0.0.saturating_sub(1).0 range
546                    // (assuming a load of 1.0 per CPU core is "fully loaded")
547                    let num_cpus = num_cpus::get() as f64;
548                    return (loadavg[0] / num_cpus).min(1.0);
549                }
550            }
551
552            // Fallback value if we can't get actual CPU usage
553            0.5
554        }
555    }
556
557    fn get_memoryinfo(&self) -> (u64, u64) {
558        // Try to get memory info if sysinfo is available
559        #[cfg(feature = "sysinfo")]
560        {
561            use sysinfo::System;
562            let mut system = System::new_all();
563            system.refresh_memory();
564
565            (
566                system.used_memory() * 1024,
567                system.available_memory() * 1024,
568            )
569        }
570
571        // Fallback to reasonable defaults
572        #[cfg(not(feature = "sysinfo"))]
573        {
574            // Check if we have sys-info available
575            #[cfg(feature = "sysinfo")]
576            {
577                if let Ok(mem) = sys_info::mem_info() {
578                    let used = (mem.total - mem.free) * 1024;
579                    let available = mem.free * 1024;
580                    return (used, available);
581                }
582            }
583
584            // Fallback values if we can't get actual memory info
585            // Assume 50% of memory is being used
586            (4 * 1024 * 1024 * 1024, 4 * 1024 * 1024 * 1024) // 4GB used, 4GB available
587        }
588    }
589
590    fn get_io_stats(&self) -> (u64, u64) {
591        // Try to get IO stats if sysinfo is available
592        #[cfg(feature = "sysinfo")]
593        {
594            use sysinfo::{Disks, System};
595            let system = System::new_all();
596            let disks = Disks::new_with_refreshed_list();
597
598            // Sum IO activity across all disks
599            let mut total_ops = 0;
600            let mut total_bytes = 0;
601
602            for disk in disks.list() {
603                // Simple approximation of IO ops
604                total_ops += 1; // Just a placeholder since sysinfo doesn't have this info
605                                // Note: sysinfo disk API doesn't provide read/write bytes directly in recent versions
606                                // Using available space as an approximation for I/O calculation
607                total_bytes += disk.available_space();
608            }
609
610            (total_ops, total_bytes)
611        }
612
613        // Fallback to reasonable defaults
614        #[cfg(not(feature = "sysinfo"))]
615        {
616            (10, 1024 * 1024) // 10 ops/sec, 1MB/sec
617        }
618    }
619}
620
621/// Resource-aware prefetching manager.
622pub struct ResourceAwarePrefetcher {
623    /// Resource monitor
624    monitor: ResourceMonitor,
625
626    /// Base prefetching configuration
627    baseconfig: PrefetchConfig,
628
629    /// Current prefetching configuration (adjusted for resources)
630    currentconfig: PrefetchConfig,
631
632    /// Whether prefetching is currently enabled
633    enabled: bool,
634
635    /// Performance statistics
636    performance_stats: Arc<Mutex<PerformanceStats>>,
637
638    /// Last time stats were updated
639    last_stats_update: Instant,
640}
641
642/// Performance statistics for prefetching.
643#[derive(Debug, Clone, Default)]
644pub struct PerformanceStats {
645    /// Prefetch hit rate
646    pub hit_rate: f64,
647
648    /// Average latency for prefetched blocks
649    pub prefetch_latency_ns: f64,
650
651    /// Average latency for non-prefetched blocks
652    pub non_prefetch_latency_ns: f64,
653
654    /// Number of blocks prefetched
655    pub prefetch_count: usize,
656
657    /// Number of blocks accessed
658    pub access_count: usize,
659
660    /// Resource summaries at different points in time
661    pub resource_snapshots: Vec<(Instant, ResourceSummary)>,
662}
663
664impl ResourceAwarePrefetcher {
665    /// Create a new resource-aware prefetcher.
666    pub fn config(baseconfig: PrefetchConfig, resourceconfig: ResourceAwareConfig) -> Self {
667        Self {
668            monitor: ResourceMonitor::new(resourceconfig),
669            baseconfig: baseconfig.clone(),
670            currentconfig: baseconfig,
671            enabled: true,
672            performance_stats: Arc::new(Mutex::new(PerformanceStats::default())),
673            last_stats_update: Instant::now(),
674        }
675    }
676
677    /// Update prefetching configuration based on resource pressure.
678    pub fn update_config(&mut self) -> bool {
679        if !self.enabled {
680            return false;
681        }
682
683        // Check if we should adjust prefetching based on resources
684        let mut config = self.currentconfig.clone();
685        let changed = self.monitor.adjust_prefetch_config(&mut config);
686
687        if changed {
688            self.currentconfig = config;
689
690            // Take a resource snapshot and record it with stats
691            if let Some(_snapshot) = self.monitor.get_latest_snapshot() {
692                let summary = self.monitor.get_resource_summary();
693                if let Ok(mut stats) = self.performance_stats.lock() {
694                    stats.resource_snapshots.push((Instant::now(), summary));
695
696                    // Limit the number of snapshots
697                    while stats.resource_snapshots.len() > 10 {
698                        stats.resource_snapshots.remove(0);
699                    }
700                }
701            }
702        }
703
704        changed
705    }
706
707    /// Record performance data from prefetching.
708    pub fn record_prefetch_performance(
709        &mut self,
710        is_prefetched: bool,
711        latency_ns: f64,
712        prefetch_stats: &PrefetchStats,
713    ) {
714        if let Ok(mut stats) = self.performance_stats.lock() {
715            // Update overall stats
716            stats.hit_rate = prefetch_stats.hit_rate;
717            stats.prefetch_count = prefetch_stats.prefetch_count;
718            stats.access_count = prefetch_stats.prefetch_hits + prefetch_stats.prefetch_misses;
719
720            // Update latency based on whether this was a prefetched block
721            if is_prefetched {
722                // Moving average for prefetch latency
723                if stats.prefetch_latency_ns == 0.0 {
724                    stats.prefetch_latency_ns = latency_ns;
725                } else {
726                    stats.prefetch_latency_ns = stats.prefetch_latency_ns * 0.9 + latency_ns * 0.1;
727                }
728            } else {
729                // Moving average for non-prefetch latency
730                if stats.non_prefetch_latency_ns == 0.0 {
731                    stats.non_prefetch_latency_ns = latency_ns;
732                } else {
733                    stats.non_prefetch_latency_ns =
734                        stats.non_prefetch_latency_ns * 0.9 + latency_ns * 0.1;
735                }
736            }
737        }
738
739        // Take resource snapshots periodically
740        if self.last_stats_update.elapsed() >= Duration::from_secs(5) {
741            self.last_stats_update = Instant::now();
742
743            // Take a snapshot
744            let summary = self.monitor.get_resource_summary();
745            if let Ok(mut stats) = self.performance_stats.lock() {
746                stats.resource_snapshots.push((Instant::now(), summary));
747
748                // Limit the number of snapshots
749                while stats.resource_snapshots.len() > 10 {
750                    stats.resource_snapshots.remove(0);
751                }
752            }
753        }
754    }
755
756    /// Get the current prefetching configuration.
757    pub fn get_currentconfig(&self) -> PrefetchConfig {
758        self.currentconfig.clone()
759    }
760
761    /// Get the base prefetching configuration.
762    pub fn getbaseconfig(&self) -> PrefetchConfig {
763        self.baseconfig.clone()
764    }
765
766    /// Get a snapshot of the current resource usage.
767    pub fn take_resource_snapshot(&mut self) -> ResourceSnapshot {
768        self.monitor.take_snapshot()
769    }
770
771    /// Get a summary of resource usage.
772    pub fn get_resource_summary(&self) -> ResourceSummary {
773        self.monitor.get_resource_summary()
774    }
775
776    /// Get the performance statistics.
777    pub fn get_performance_stats(&self) -> PerformanceStats {
778        if let Ok(stats) = self.performance_stats.lock() {
779            stats.clone()
780        } else {
781            PerformanceStats::default()
782        }
783    }
784
785    /// Check if the system is under pressure.
786    pub fn is_under_pressure(&mut self) -> bool {
787        self.monitor.is_under_pressure()
788    }
789
790    /// Enable or disable prefetching.
791    pub fn set_enabled(&mut self, enabled: bool) {
792        self.enabled = enabled;
793    }
794
795    /// Check if prefetching is enabled.
796    pub fn is_enabled(&self) -> bool {
797        self.enabled
798    }
799
800    /// Get the optimal prefetch count based on current resources.
801    pub fn get_optimal_prefetch_count(&mut self) -> usize {
802        self.monitor
803            .get_optimal_prefetch_count(self.baseconfig.prefetch_count)
804    }
805
806    /// Reset the prefetching configuration to the base configuration.
807    pub fn reset_config(&mut self) {
808        self.currentconfig = self.baseconfig.clone();
809    }
810}
811
812/// Enhanced prefetching configuration with resource awareness.
813#[derive(Debug, Clone)]
814#[allow(dead_code)]
815pub struct ResourceAwarePrefetchingConfig {
816    /// Base prefetching configuration
817    pub baseconfig: PrefetchConfig,
818
819    /// Resource awareness configuration
820    pub resourceconfig: ResourceAwareConfig,
821}
822
823#[allow(dead_code)]
824impl ResourceAwarePrefetchingConfig {
825    /// Create a new resource-aware prefetching configuration.
826    pub fn config(baseconfig: PrefetchConfig, resourceconfig: ResourceAwareConfig) -> Self {
827        Self {
828            baseconfig,
829            resourceconfig,
830        }
831    }
832
833    /// Create a resource-aware prefetcher from this configuration.
834    pub fn create_prefetcher(&self) -> ResourceAwarePrefetcher {
835        ResourceAwarePrefetcher::config(self.baseconfig.clone(), self.resourceconfig.clone())
836    }
837}
838
839#[cfg(test)]
840mod tests {
841    use super::*;
842
843    // Mock implementation of SystemInfo for testing
844    struct MockSystemInfo {
845        cpu_usage: f64,
846        memory_used: u64,
847        memory_available: u64,
848        io_ops: u64,
849        io_bytes: u64,
850    }
851
852    impl SystemInfo for MockSystemInfo {
853        fn get_cpu_usage(&self) -> f64 {
854            self.cpu_usage
855        }
856
857        fn get_memoryinfo(&self) -> (u64, u64) {
858            (self.memory_used, self.memory_available)
859        }
860
861        fn get_io_stats(&self) -> (u64, u64) {
862            (self.io_ops, self.io_bytes)
863        }
864    }
865
866    impl MockSystemInfo {
867        fn new(
868            cpu_usage: f64,
869            memory_used: u64,
870            memory_available: u64,
871            io_ops: u64,
872            io_bytes: u64,
873        ) -> Self {
874            Self {
875                cpu_usage,
876                memory_used,
877                memory_available,
878                io_ops,
879                io_bytes,
880            }
881        }
882
883        fn bytes(value: u64) -> Self {
884            Self {
885                cpu_usage: 0.0,
886                memory_used: value,
887                memory_available: 1024 * 1024 * 1024, // 1GB default
888                io_ops: 0,
889                io_bytes: 0,
890            }
891        }
892    }
893
894    #[test]
895    fn test_resource_snapshot() {
896        let snapshot = ResourceSnapshot {
897            timestamp: Instant::now(),
898            cpu_usage: 0.7,
899            memory_usage: 8 * 1024 * 1024 * 1024,     // 8 GB
900            memory_available: 8 * 1024 * 1024 * 1024, // 8 GB
901            io_ops_per_sec: 100,
902            io_bytes_per_sec: 10 * 1024 * 1024, // 10 MB/s
903        };
904
905        // Check memory pressure
906        assert_eq!(snapshot.memory_pressure(), 0.5); // 8GB / (8GB + 8GB) = 0.5
907
908        // Check combined pressure
909        let combined = snapshot.combined_pressure();
910        assert!(combined > 0.0 && combined < 1.0);
911    }
912
913    #[test]
914    fn test_optimal_prefetch_count() {
915        // Create a resource monitor with custom system info
916        let config = ResourceAwareConfig {
917            auto_adjust: true,
918            min_prefetch_count: 1,
919            max_prefetch_count: 10,
920            ..Default::default()
921        };
922
923        let mut monitor = ResourceMonitor::new(config);
924
925        // Replace the system info with our mock
926        monitor.sys_info = Box::new(MockSystemInfo::new(
927            0.2,                     // Low CPU usage
928            2 * 1024 * 1024 * 1024,  // 2 GB used memory
929            14 * 1024 * 1024 * 1024, // 14 GB available memory
930            10,                      // 10 IO ops/sec
931            1024 * 1024,             // 1 MB/s IO
932        ));
933
934        // Take a snapshot
935        monitor.take_snapshot();
936
937        // Check optimal prefetch count with low pressure
938        let base_count = 4;
939        let optimal = monitor.get_optimal_prefetch_count(base_count);
940        assert!(optimal >= base_count); // Should be the same or higher under low pressure
941
942        // Now test with high pressure
943        monitor.sys_info = Box::new(MockSystemInfo::new(
944            0.9,                     // High CPU usage
945            14 * 1024 * 1024 * 1024, // 14 GB used memory
946            2 * 1024 * 1024 * 1024,  // 2 GB available memory
947            1000,                    // 1000 IO ops/sec
948            100 * 1024 * 1024,       // 100 MB/s IO
949        ));
950
951        // Take a snapshot
952        monitor.take_snapshot();
953
954        // Check optimal prefetch count with high pressure
955        let optimal = monitor.get_optimal_prefetch_count(base_count);
956        assert!(optimal <= base_count); // Should be lower under high pressure
957    }
958
959    #[test]
960    fn test_resource_aware_prefetcher() {
961        // Create a resource-aware prefetcher
962        let baseconfig = PrefetchConfig {
963            prefetch_count: 5,
964            ..Default::default()
965        };
966
967        let resource_config = ResourceAwareConfig {
968            auto_adjust: true,
969            min_prefetch_count: 1,
970            max_prefetch_count: 10,
971            ..Default::default()
972        };
973
974        let mut prefetcher = ResourceAwarePrefetcher::config(baseconfig, resource_config);
975
976        // Record some performance data
977        let stats = PrefetchStats {
978            prefetch_count: 100,
979            prefetch_hits: 80,
980            prefetch_misses: 20,
981            hit_rate: 0.8,
982        };
983
984        prefetcher.record_prefetch_performance(true, 500_000.0, &stats); // 500µs latency, prefetched
985        prefetcher.record_prefetch_performance(false, 2_000_000.0, &stats); // 2ms latency, not prefetched
986
987        // Get the performance stats
988        let perf_stats = prefetcher.get_performance_stats();
989        assert_eq!(perf_stats.hit_rate, 0.8);
990        assert!(perf_stats.prefetch_latency_ns > 0.0);
991        assert!(perf_stats.non_prefetch_latency_ns > 0.0);
992        assert!(perf_stats.non_prefetch_latency_ns > perf_stats.prefetch_latency_ns);
993        // Non-prefetched should be slower
994    }
995}