scirs2_io/pipeline/advanced_optimization/
monitoring.rs1use crate::error::{IoError, Result};
7use std::sync::{Arc, RwLock};
8use std::time::{Duration, Instant};
9
10use super::config::{CachePerformance, MemoryUsage, NumaTopology, SystemMetrics};
11
12#[derive(Debug)]
14pub struct ResourceMonitor {
15 last_update: Instant,
17 update_frequency: Duration,
19 cached_metrics: Option<SystemMetrics>,
21 metrics_history: Vec<SystemMetrics>,
23 max_history_size: usize,
25}
26
27impl Default for ResourceMonitor {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl ResourceMonitor {
34 pub fn new() -> Self {
35 Self {
36 last_update: Instant::now(),
37 update_frequency: Duration::from_millis(500), cached_metrics: None,
39 metrics_history: Vec::new(),
40 max_history_size: 100, }
42 }
43
44 pub fn get_current_metrics(&mut self) -> Result<SystemMetrics> {
46 let now = Instant::now();
47
48 if self.cached_metrics.is_none()
50 || now.duration_since(self.last_update) >= self.update_frequency
51 {
52 let metrics = self.collect_system_metrics()?;
53 self.cached_metrics = Some(metrics.clone());
54 self.last_update = now;
55
56 self.metrics_history.push(metrics.clone());
58 if self.metrics_history.len() > self.max_history_size {
59 self.metrics_history.remove(0);
60 }
61
62 Ok(metrics)
63 } else {
64 Ok(self.cached_metrics.as_ref().unwrap().clone())
65 }
66 }
67
68 fn collect_system_metrics(&self) -> Result<SystemMetrics> {
70 Ok(SystemMetrics {
71 cpu_usage: self.get_cpu_usage()?,
72 memory_usage: self.get_memory_usage()?,
73 io_utilization: self.get_io_utilization()?,
74 network_bandwidth_usage: self.get_network_usage()?,
75 cache_performance: self.get_cache_performance()?,
76 numa_topology: self.get_numa_topology()?,
77 })
78 }
79
80 fn get_cpu_usage(&self) -> Result<f64> {
82 #[cfg(target_os = "linux")]
83 {
84 self.get_linux_cpu_usage()
85 }
86 #[cfg(target_os = "windows")]
87 {
88 self.get_windows_cpu_usage()
89 }
90 #[cfg(target_os = "macos")]
91 {
92 self.get_macos_cpu_usage()
93 }
94 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
95 {
96 Ok(0.5) }
98 }
99
100 #[cfg(target_os = "linux")]
101 fn get_linux_cpu_usage(&self) -> Result<f64> {
102 let stat_content = std::fs::read_to_string("/proc/stat")
104 .map_err(|e| IoError::Other(format!("Failed to read /proc/stat: {}", e)))?;
105
106 if let Some(cpu_line) = stat_content.lines().next() {
107 let values: Vec<u64> = cpu_line
108 .split_whitespace()
109 .skip(1)
110 .take(4)
111 .filter_map(|s| s.parse().ok())
112 .collect();
113
114 if values.len() >= 4 {
115 let idle = values[3];
116 let total: u64 = values.iter().sum();
117 return Ok(1.0 - (idle as f64) / (total as f64));
118 }
119 }
120
121 Ok(0.5) }
123
124 #[cfg(target_os = "windows")]
125 fn get_windows_cpu_usage(&self) -> Result<f64> {
126 Ok(0.5)
129 }
130
131 #[cfg(target_os = "macos")]
132 fn get_macos_cpu_usage(&self) -> Result<f64> {
133 Ok(0.5)
136 }
137
138 fn get_memory_usage(&self) -> Result<MemoryUsage> {
140 #[cfg(target_os = "linux")]
141 {
142 self.get_linux_memory_usage()
143 }
144 #[cfg(not(target_os = "linux"))]
145 {
146 Ok(MemoryUsage {
147 total: 8 * 1024 * 1024 * 1024, available: 4 * 1024 * 1024 * 1024, used: 4 * 1024 * 1024 * 1024,
150 utilization: 0.5,
151 })
152 }
153 }
154
155 #[cfg(target_os = "linux")]
156 fn get_linux_memory_usage(&self) -> Result<MemoryUsage> {
157 let meminfo_content = std::fs::read_to_string("/proc/meminfo")
158 .map_err(|e| IoError::Other(format!("Failed to read /proc/meminfo: {}", e)))?;
159
160 let mut total = 0u64;
161 let mut available = 0u64;
162
163 for line in meminfo_content.lines() {
164 if line.starts_with("MemTotal:") {
165 total = line
166 .split_whitespace()
167 .nth(1)
168 .and_then(|s| s.parse().ok())
169 .unwrap_or(0)
170 * 1024; } else if line.starts_with("MemAvailable:") {
172 available = line
173 .split_whitespace()
174 .nth(1)
175 .and_then(|s| s.parse().ok())
176 .unwrap_or(0)
177 * 1024; }
179 }
180
181 let used = total - available;
182 let utilization = if total > 0 {
183 used as f64 / total as f64
184 } else {
185 0.0
186 };
187
188 Ok(MemoryUsage {
189 total,
190 available,
191 used,
192 utilization,
193 })
194 }
195
196 fn get_io_utilization(&self) -> Result<f64> {
198 #[cfg(target_os = "linux")]
200 {
201 self.get_linux_io_utilization()
202 }
203 #[cfg(not(target_os = "linux"))]
204 {
205 Ok(0.3) }
207 }
208
209 #[cfg(target_os = "linux")]
210 fn get_linux_io_utilization(&self) -> Result<f64> {
211 match std::fs::read_to_string("/proc/diskstats") {
214 Ok(content) => {
215 Ok(0.3)
218 }
219 Err(_) => Ok(0.3), }
221 }
222
223 fn get_network_usage(&self) -> Result<f64> {
225 #[cfg(target_os = "linux")]
227 {
228 self.get_linux_network_usage()
229 }
230 #[cfg(not(target_os = "linux"))]
231 {
232 Ok(0.2) }
234 }
235
236 #[cfg(target_os = "linux")]
237 fn get_linux_network_usage(&self) -> Result<f64> {
238 match std::fs::read_to_string("/proc/net/dev") {
241 Ok(content) => {
242 Ok(0.2)
245 }
246 Err(_) => Ok(0.2), }
248 }
249
250 fn get_cache_performance(&self) -> Result<CachePerformance> {
252 Ok(CachePerformance {
255 l1_hit_rate: 0.95,
256 l2_hit_rate: 0.85,
257 l3_hit_rate: 0.75,
258 tlb_hit_rate: 0.99,
259 })
260 }
261
262 fn get_numa_topology(&self) -> Result<NumaTopology> {
264 #[cfg(target_os = "linux")]
265 {
266 self.get_linux_numa_topology()
267 }
268 #[cfg(not(target_os = "linux"))]
269 {
270 Ok(NumaTopology::default())
271 }
272 }
273
274 #[cfg(target_os = "linux")]
275 fn get_linux_numa_topology(&self) -> Result<NumaTopology> {
276 match std::fs::read_dir("/sys/devices/system/node/") {
279 Ok(_entries) => {
280 Ok(NumaTopology::default())
283 }
284 Err(_) => Ok(NumaTopology::default()),
285 }
286 }
287
288 pub fn get_metrics_trend(&self, duration: Duration) -> Vec<&SystemMetrics> {
290 let cutoff_time = Instant::now() - duration;
291 self.metrics_history.iter().collect()
294 }
295
296 pub fn is_high_load(&self) -> bool {
298 if let Some(metrics) = &self.cached_metrics {
299 metrics.cpu_usage > 0.8
300 || metrics.memory_usage.utilization > 0.9
301 || metrics.io_utilization > 0.8
302 } else {
303 false
304 }
305 }
306
307 pub fn get_utilization_score(&self) -> f64 {
309 if let Some(metrics) = &self.cached_metrics {
310 (metrics.cpu_usage + metrics.memory_usage.utilization + metrics.io_utilization) / 3.0
311 } else {
312 0.5
313 }
314 }
315
316 pub fn predict_resource_pressure(&self, lookahead: Duration) -> f64 {
318 if self.metrics_history.len() < 2 {
320 return self.get_utilization_score();
321 }
322
323 let recent_scores: Vec<f64> = self
324 .metrics_history
325 .iter()
326 .rev()
327 .take(10)
328 .map(|m| (m.cpu_usage + m.memory_usage.utilization + m.io_utilization) / 3.0)
329 .collect();
330
331 if recent_scores.len() < 2 {
332 return recent_scores[0];
333 }
334
335 let n = recent_scores.len() as f64;
337 let sum_x: f64 = (0..recent_scores.len()).map(|i| i as f64).sum();
338 let sum_y: f64 = recent_scores.iter().sum();
339 let sum_xy: f64 = recent_scores
340 .iter()
341 .enumerate()
342 .map(|(i, &y)| i as f64 * y)
343 .sum();
344 let sum_x2: f64 = (0..recent_scores.len()).map(|i| (i as f64).powi(2)).sum();
345
346 let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x * sum_x);
347 let intercept = (sum_y - slope * sum_x) / n;
348
349 let future_steps = lookahead.as_secs() as f64 / self.update_frequency.as_secs() as f64;
351 let predicted = intercept + slope * (n + future_steps);
352
353 predicted.clamp(0.0, 1.0)
354 }
355}