1use crate::error::{CoreError, CoreResult};
8use crate::performance::{OptimizationSettings, PerformanceProfile, WorkloadType};
9use std::collections::{HashMap, VecDeque};
10use std::sync::{Arc, Mutex, RwLock};
11use std::thread;
12use std::time::{Duration, Instant};
13
14static GLOBAL_RESOURCE_MANAGER: std::sync::OnceLock<Arc<ResourceManager>> =
16 std::sync::OnceLock::new();
17
18#[derive(Debug)]
20pub struct ResourceManager {
21 allocator: Arc<Mutex<AdaptiveAllocator>>,
22 tuner: Arc<RwLock<AutoTuner>>,
23 monitor: Arc<Mutex<ResourceMonitor>>,
24 policies: Arc<RwLock<ResourcePolicies>>,
25}
26
27impl ResourceManager {
28 pub fn new() -> CoreResult<Self> {
30 let performance_profile = PerformanceProfile::detect();
31
32 Ok(Self {
33 allocator: Arc::new(Mutex::new(AdaptiveAllocator::new(
34 performance_profile.clone(),
35 )?)),
36 tuner: Arc::new(RwLock::new(AutoTuner::new(performance_profile.clone())?)),
37 monitor: Arc::new(Mutex::new(ResourceMonitor::new()?)),
38 policies: Arc::new(RwLock::new(ResourcePolicies::default())),
39 })
40 }
41
42 pub fn global() -> CoreResult<Arc<Self>> {
44 Ok(GLOBAL_RESOURCE_MANAGER
45 .get_or_init(|| Arc::new(Self::new().expect("Operation failed")))
46 .clone())
47 }
48
49 pub fn start(&self) -> CoreResult<()> {
51 let monitor = self.monitor.clone();
53 let policies = self.policies.clone();
54 let tuner = self.tuner.clone();
55
56 thread::spawn(move || loop {
57 if let Err(e) = Self::monitoring_loop(&monitor, &policies, &tuner) {
58 eprintln!("Resource monitoring error: {e:?}");
59 }
60 thread::sleep(Duration::from_secs(10));
61 });
62
63 let tuner_clone = self.tuner.clone();
65 let monitor_clone = self.monitor.clone();
66
67 thread::spawn(move || loop {
68 if let Err(e) = Self::tuning_loop(&tuner_clone, &monitor_clone) {
69 eprintln!("Auto-tuning error: {e:?}");
70 }
71 thread::sleep(Duration::from_secs(30));
72 });
73
74 Ok(())
75 }
76
77 fn monitoring_loop(
78 monitor: &Arc<Mutex<ResourceMonitor>>,
79 policies: &Arc<RwLock<ResourcePolicies>>,
80 tuner: &Arc<RwLock<AutoTuner>>,
81 ) -> CoreResult<()> {
82 let mut monitor = monitor.lock().expect("Operation failed");
83 let metrics = monitor.collect_metrics()?;
84
85 let policies = policies.read().expect("Operation failed");
87 if let Some(action) = policies.check_violations(&metrics)? {
88 match action {
89 PolicyAction::ScaleUp => {
90 let mut tuner = tuner.write().expect("Operation failed");
91 (*tuner).increase_resources(&metrics)?;
92 }
93 PolicyAction::ScaleDown => {
94 let mut tuner = tuner.write().expect("Operation failed");
95 (*tuner).decrease_resources(&metrics)?;
96 }
97 PolicyAction::Optimize => {
98 let mut tuner = tuner.write().expect("Operation failed");
99 tuner.optimize_configuration(&metrics)?;
100 }
101 PolicyAction::Alert => {
102 monitor.trigger_alert(&metrics)?;
103 }
104 }
105 }
106
107 Ok(())
108 }
109
110 fn tuning_loop(
111 tuner: &Arc<RwLock<AutoTuner>>,
112 monitor: &Arc<Mutex<ResourceMonitor>>,
113 ) -> CoreResult<()> {
114 let metrics = {
115 let monitor = monitor.lock().expect("Operation failed");
116 monitor.get_current_metrics()?
117 };
118
119 let mut tuner = tuner.write().expect("Operation failed");
120 tuner.adaptive_optimization(&metrics)?;
121
122 Ok(())
123 }
124
125 pub fn allocate_optimized<T>(
127 &self,
128 size: usize,
129 workload_type: WorkloadType,
130 ) -> CoreResult<OptimizedAllocation<T>> {
131 let mut allocator = self.allocator.lock().expect("Operation failed");
132 allocator.allocate_optimized(size, workload_type)
133 }
134
135 pub fn get_utilization(&self) -> CoreResult<ResourceUtilization> {
137 let monitor = self.monitor.lock().expect("Operation failed");
138 monitor.get_current_utilization()
139 }
140
141 pub fn updatepolicies(&self, newpolicies: ResourcePolicies) -> CoreResult<()> {
143 let mut policies = self.policies.write().expect("Operation failed");
144 *policies = newpolicies;
145 Ok(())
146 }
147
148 pub fn get_recommendations(&self) -> CoreResult<Vec<TuningRecommendation>> {
150 let tuner = self.tuner.read().expect("Operation failed");
151 tuner.get_recommendations()
152 }
153}
154
155#[derive(Debug)]
157pub struct AdaptiveAllocator {
158 #[allow(dead_code)]
159 performance_profile: PerformanceProfile,
160 allocation_patterns: HashMap<WorkloadType, AllocationPattern>,
161 memory_pools: HashMap<String, MemoryPool>,
162 total_allocated: usize,
163 peak_allocated: usize,
164}
165
166#[derive(Debug, Clone)]
167struct AllocationPattern {
168 #[allow(dead_code)]
169 typical_size: usize,
170 #[allow(dead_code)]
171 typical_lifetime: Duration,
172 access_pattern: AccessPattern,
173 alignment_requirement: usize,
174}
175
176#[derive(Debug, Clone, Copy, PartialEq, Eq)]
177enum AccessPattern {
178 Sequential,
179 Random,
180 #[allow(dead_code)]
181 Strided,
182 Temporal,
183}
184
185impl AdaptiveAllocator {
186 pub fn new(performanceprofile: PerformanceProfile) -> CoreResult<Self> {
187 let mut allocator = Self {
188 performance_profile: performanceprofile,
189 allocation_patterns: HashMap::new(),
190 memory_pools: HashMap::new(),
191 total_allocated: 0,
192 peak_allocated: 0,
193 };
194
195 allocator.initialize_patterns()?;
197
198 Ok(allocator)
199 }
200
201 fn initialize_patterns(&mut self) -> CoreResult<()> {
202 self.allocation_patterns.insert(
204 WorkloadType::LinearAlgebra,
205 AllocationPattern {
206 typical_size: 1024 * 1024, typical_lifetime: Duration::from_secs(60),
208 access_pattern: AccessPattern::Sequential,
209 alignment_requirement: 64, },
211 );
212
213 self.allocation_patterns.insert(
215 WorkloadType::Statistics,
216 AllocationPattern {
217 typical_size: 64 * 1024, typical_lifetime: Duration::from_secs(30),
219 access_pattern: AccessPattern::Random,
220 alignment_requirement: 32,
221 },
222 );
223
224 self.allocation_patterns.insert(
226 WorkloadType::SignalProcessing,
227 AllocationPattern {
228 typical_size: 256 * 1024, typical_lifetime: Duration::from_secs(45),
230 access_pattern: AccessPattern::Temporal,
231 alignment_requirement: 64,
232 },
233 );
234
235 Ok(())
236 }
237
238 pub fn allocate_optimized<T>(
239 &mut self,
240 size: usize,
241 workload_type: WorkloadType,
242 ) -> CoreResult<OptimizedAllocation<T>> {
243 let pattern = self
244 .allocation_patterns
245 .get(&workload_type)
246 .cloned()
247 .unwrap_or_else(|| AllocationPattern {
248 typical_size: size,
249 typical_lifetime: Duration::from_secs(60),
250 access_pattern: AccessPattern::Sequential,
251 alignment_requirement: std::mem::align_of::<T>(),
252 });
253
254 let strategy = self.choose_allocation_strategy(size, &pattern)?;
256
257 let allocation = match strategy {
259 AllocationStrategy::Pool(pool_name) => self.allocate_from_pool(&pool_name, size)?,
260 AllocationStrategy::Direct => {
261 self.allocate_direct(size, pattern.alignment_requirement)?
262 }
263 AllocationStrategy::MemoryMapped => self.allocate_memory_mapped(size)?,
264 };
265
266 self.total_allocated += size * std::mem::size_of::<T>();
267 self.peak_allocated = self.peak_allocated.max(self.total_allocated);
268
269 Ok(allocation)
270 }
271
272 fn choose_allocation_strategy(
273 &self,
274 size: usize,
275 pattern: &AllocationPattern,
276 ) -> CoreResult<AllocationStrategy> {
277 let size_bytes = size * std::mem::size_of::<u8>();
278
279 if size_bytes > 100 * 1024 * 1024 {
281 return Ok(AllocationStrategy::MemoryMapped);
283 }
284
285 if size_bytes > 1024 && size_bytes < 10 * 1024 * 1024 {
287 let pool_name = format!("{}_{}", size_bytes / 1024, pattern.access_pattern as u8);
289 return Ok(AllocationStrategy::Pool(pool_name));
290 }
291
292 Ok(AllocationStrategy::Direct)
294 }
295
296 fn allocate_from_pool<T>(
297 &mut self,
298 pool_name: &str,
299 size: usize,
300 ) -> CoreResult<OptimizedAllocation<T>> {
301 if !self.memory_pools.contains_key(pool_name) {
303 let pool = MemoryPool::new(size * std::mem::size_of::<T>(), 10)?; self.memory_pools.insert(pool_name.to_string(), pool);
305 }
306
307 let pool = self
308 .memory_pools
309 .get_mut(pool_name)
310 .expect("Operation failed");
311 let ptr = pool.allocate(size * std::mem::size_of::<T>())?;
312
313 Ok(OptimizedAllocation {
314 ptr: ptr as *mut T,
315 size,
316 allocation_type: AllocationType::Pool(pool_name.to_string()),
317 alignment: 64,
318 })
319 }
320
321 fn allocate_direct<T>(
322 &self,
323 size: usize,
324 alignment: usize,
325 ) -> CoreResult<OptimizedAllocation<T>> {
326 let layout = std::alloc::Layout::from_size_align(
327 size * std::mem::size_of::<T>(),
328 alignment.max(std::mem::align_of::<T>()),
329 )
330 .map_err(|_| {
331 CoreError::AllocationError(crate::error::ErrorContext::new("Invalid layout"))
332 })?;
333
334 let ptr = unsafe { std::alloc::alloc(layout) as *mut T };
335 if ptr.is_null() {
336 return Err(CoreError::AllocationError(crate::error::ErrorContext::new(
337 "Allocation failed",
338 )));
339 }
340
341 Ok(OptimizedAllocation {
342 ptr,
343 size,
344 allocation_type: AllocationType::Direct(layout),
345 alignment,
346 })
347 }
348
349 fn allocate_memory_mapped<T>(&self, size: usize) -> CoreResult<OptimizedAllocation<T>> {
350 self.allocate_direct(size, 64)
353 }
354}
355
356#[derive(Debug)]
358pub struct OptimizedAllocation<T> {
359 ptr: *mut T,
360 size: usize,
361 allocation_type: AllocationType,
362 alignment: usize,
363}
364
365#[derive(Debug)]
366enum AllocationType {
367 Direct(std::alloc::Layout),
368 #[allow(dead_code)]
369 Pool(String),
370 #[allow(dead_code)]
371 MemoryMapped,
372}
373
374#[derive(Debug)]
375enum AllocationStrategy {
376 Direct,
377 Pool(String),
378 MemoryMapped,
379}
380
381impl<T> OptimizedAllocation<T> {
382 pub fn as_ptr(&self) -> *mut T {
384 self.ptr
385 }
386
387 pub fn size(&self) -> usize {
389 self.size
390 }
391
392 pub fn alignment(&self) -> usize {
394 self.alignment
395 }
396
397 pub fn is_cache_aligned(&self) -> bool {
399 self.alignment >= 64
400 }
401}
402
403impl<T> Drop for OptimizedAllocation<T> {
404 fn drop(&mut self) {
405 match &self.allocation_type {
406 AllocationType::Direct(layout) => unsafe {
407 std::alloc::dealloc(self.ptr as *mut u8, *layout);
408 },
409 AllocationType::Pool(_) => {
410 }
412 AllocationType::MemoryMapped => {
413 }
415 }
416 }
417}
418
419#[derive(Debug)]
421struct MemoryPool {
422 block_size: usize,
423 blocks: VecDeque<*mut u8>,
424 allocated_blocks: Vec<*mut u8>,
425}
426
427unsafe impl Send for MemoryPool {}
430unsafe impl Sync for MemoryPool {}
431
432impl MemoryPool {
433 fn new(block_size: usize, initial_blockcount: usize) -> CoreResult<Self> {
434 let mut pool = Self {
435 block_size,
436 blocks: VecDeque::new(),
437 allocated_blocks: Vec::new(),
438 };
439
440 for _ in 0..initial_blockcount {
442 pool.add_block()?;
443 }
444
445 Ok(pool)
446 }
447
448 fn add_block(&mut self) -> CoreResult<()> {
449 let layout = std::alloc::Layout::from_size_align(self.block_size, 64).map_err(|_| {
450 CoreError::AllocationError(crate::error::ErrorContext::new("Invalid layout"))
451 })?;
452
453 let ptr = unsafe { std::alloc::alloc(layout) };
454 if ptr.is_null() {
455 return Err(CoreError::AllocationError(crate::error::ErrorContext::new(
456 "Pool block allocation failed",
457 )));
458 }
459
460 self.blocks.push_back(ptr);
461 self.allocated_blocks.push(ptr);
462 Ok(())
463 }
464
465 fn allocate(&mut self, size: usize) -> CoreResult<*mut u8> {
466 if size > self.block_size {
467 return Err(CoreError::AllocationError(crate::error::ErrorContext::new(
468 "Requested size exceeds block size",
469 )));
470 }
471
472 if self.blocks.is_empty() {
473 self.add_block()?;
474 }
475
476 Ok(self.blocks.pop_front().expect("Operation failed"))
477 }
478
479 #[allow(dead_code)]
480 fn deallocate(&mut self, ptr: *mut u8) {
481 self.blocks.push_back(ptr);
482 }
483}
484
485impl Drop for MemoryPool {
486 fn drop(&mut self) {
487 for &ptr in &self.allocated_blocks {
488 unsafe {
489 let layout = std::alloc::Layout::from_size_align(self.block_size, 64)
490 .expect("Operation failed");
491 std::alloc::dealloc(ptr, layout);
492 }
493 }
494 }
495}
496
497#[derive(Debug)]
499pub struct AutoTuner {
500 performance_profile: PerformanceProfile,
501 optimization_history: VecDeque<OptimizationEvent>,
502 current_settings: OptimizationSettings,
503 #[allow(dead_code)]
504 learningrate: f64,
505 #[allow(dead_code)]
506 stability_threshold: f64,
507}
508
509#[derive(Debug, Clone)]
510struct OptimizationEvent {
511 #[allow(dead_code)]
512 timestamp: Instant,
513 #[allow(dead_code)]
514 metrics_before: ResourceMetrics,
515 #[allow(dead_code)]
516 metrics_after: ResourceMetrics,
517 #[allow(dead_code)]
518 settings_applied: OptimizationSettings,
519 performance_delta: f64,
520}
521
522#[allow(dead_code)]
523impl AutoTuner {
524 pub fn new(performanceprofile: PerformanceProfile) -> CoreResult<Self> {
525 Ok(Self {
526 performance_profile: performanceprofile,
527 optimization_history: VecDeque::with_capacity(100usize),
528 current_settings: OptimizationSettings::default(),
529 learningrate: 0.1f64,
530 stability_threshold: 0.05f64, })
532 }
533
534 pub fn adaptive_optimization(&mut self, metrics: &ResourceMetrics) -> CoreResult<()> {
535 let performance_score = self.calculate_performance_score(metrics);
537
538 if self.needs_optimization(metrics, performance_score) {
540 let new_settings = self.generate_optimized_settings(metrics)?;
541 self.apply_settings(&new_settings)?;
542
543 let event = OptimizationEvent {
545 timestamp: Instant::now(),
546 metrics_before: metrics.clone(),
547 metrics_after: metrics.clone(), settings_applied: new_settings.clone(),
549 performance_delta: 0.0f64, };
551
552 self.optimization_history.push_back(event);
553 self.current_settings = new_settings;
554 }
555
556 Ok(())
557 }
558
559 fn calculate_performance_score(&self, metrics: &ResourceMetrics) -> f64 {
560 let cpu_efficiency = 1.0 - metrics.cpu_utilization;
561 let memory_efficiency = 1.0 - metrics.memory_utilization;
562 let throughput_score = metrics.operations_per_second / 1000.0f64; (cpu_efficiency + memory_efficiency + throughput_score) / 3.0
565 }
566
567 fn generate_optimized_settings(
568 &self,
569 metrics: &ResourceMetrics,
570 ) -> CoreResult<OptimizationSettings> {
571 let mut settings = self.current_settings.clone();
572
573 if metrics.cpu_utilization > 0.9 {
575 settings.num_threads = ((settings.num_threads as f64) * 0.8f64) as usize;
577 } else if metrics.cpu_utilization < 0.5 {
578 settings.num_threads = ((settings.num_threads as f64) * 1.2f64) as usize;
580 }
581
582 if metrics.memory_utilization > 0.9 {
584 settings.chunk_size = ((settings.chunk_size as f64) * 0.8f64) as usize;
586 }
587
588 if metrics.cache_miss_rate > 0.1 {
590 settings.prefetch_enabled = true;
592 settings.block_size = ((settings.block_size as f64) * 0.8f64) as usize;
593 }
594
595 Ok(settings)
596 }
597
598 fn apply_settings(&self, settings: &OptimizationSettings) -> CoreResult<()> {
599 let _ = settings.num_threads; Ok(())
606 }
607
608 pub fn optimize_configuration(&mut self, metrics: &ResourceMetrics) -> CoreResult<()> {
609 let optimized_settings = self.generate_optimized_settings(metrics)?;
610 self.apply_settings(&optimized_settings)?;
611 self.current_settings = optimized_settings;
612 Ok(())
613 }
614
615 pub fn get_recommendations(&self) -> CoreResult<Vec<TuningRecommendation>> {
616 let mut recommendations = Vec::new();
617
618 if self.optimization_history.len() >= 5 {
620 let recent_events: Vec<_> = self.optimization_history.iter().rev().take(5).collect();
621
622 if recent_events.iter().all(|e| e.performance_delta < 0.0f64) {
624 recommendations.push(TuningRecommendation {
625 category: RecommendationCategory::Performance,
626 title: "Recent optimizations showing negative returns".to_string(),
627 description: "Consider reverting to previous stable configuration".to_string(),
628 priority: RecommendationPriority::High,
629 estimated_impact: ImpactLevel::Medium,
630 });
631 }
632 }
633
634 if self.current_settings.num_threads > self.performance_profile.cpu_cores * 2 {
636 recommendations.push(TuningRecommendation {
637 category: RecommendationCategory::Resource,
638 title: "Thread count exceeds optimal range".to_string(),
639 description: format!(
640 "Current threads: {}, optimal range: 1-{}",
641 self.current_settings.num_threads,
642 self.performance_profile.cpu_cores * 2
643 ),
644 priority: RecommendationPriority::Medium,
645 estimated_impact: ImpactLevel::Low,
646 });
647 }
648
649 Ok(recommendations)
650 }
651
652 pub fn increase_resources(&mut self, metrics: &ResourceMetrics) -> CoreResult<()> {
658 let max_threads = (self.performance_profile.cpu_cores * 4).max(1);
659 let scaled_up = ((self.current_settings.num_threads as f64) * 1.2f64).round() as usize;
660 let next_threads = scaled_up.max(self.current_settings.num_threads.saturating_add(1));
664 self.current_settings.num_threads = next_threads.clamp(1, max_threads);
665 self.current_settings.chunk_size =
666 (((self.current_settings.chunk_size as f64) * 1.1f64).round() as usize).max(1);
667
668 let event = OptimizationEvent {
669 timestamp: Instant::now(),
670 metrics_before: metrics.clone(),
671 metrics_after: metrics.clone(),
672 settings_applied: self.current_settings.clone(),
673 performance_delta: 0.0,
674 };
675 self.optimization_history.push_back(event);
676 if self.optimization_history.len() > 100 {
677 self.optimization_history.pop_front();
678 }
679
680 self.apply_settings(&self.current_settings)
681 }
682
683 pub fn decrease_resources(&mut self, metrics: &ResourceMetrics) -> CoreResult<()> {
688 let max_threads = (self.performance_profile.cpu_cores * 4).max(1);
689 let scaled_down = ((self.current_settings.num_threads as f64) * 0.8f64).round() as usize;
690 self.current_settings.num_threads = scaled_down.clamp(1, max_threads);
691 self.current_settings.chunk_size =
692 (((self.current_settings.chunk_size as f64) * 0.9f64).round() as usize).max(1);
693
694 let event = OptimizationEvent {
695 timestamp: Instant::now(),
696 metrics_before: metrics.clone(),
697 metrics_after: metrics.clone(),
698 settings_applied: self.current_settings.clone(),
699 performance_delta: 0.0,
700 };
701 self.optimization_history.push_back(event);
702 if self.optimization_history.len() > 100 {
703 self.optimization_history.pop_front();
704 }
705
706 self.apply_settings(&self.current_settings)
707 }
708
709 fn needs_optimization(&mut self, metrics: &ResourceMetrics, performancescore: f64) -> bool {
717 if performancescore < 0.7 {
718 return true;
719 }
720 if metrics.cpu_utilization > 0.9 || metrics.memory_utilization > 0.9 {
721 return true;
722 }
723 if metrics.cache_miss_rate > 0.1 {
724 return true;
725 }
726 false
727 }
728}
729
730impl Default for OptimizationSettings {
731 fn default() -> Self {
732 Self {
733 use_simd: true,
734 simd_instruction_set: crate::performance::SimdInstructionSet::Scalar,
735 chunk_size: 1024,
736 block_size: 64,
737 prefetch_enabled: false,
738 parallel_threshold: 10000,
739 num_threads: std::thread::available_parallelism()
740 .map(|n| n.get())
741 .unwrap_or(1),
742 }
743 }
744}
745
746#[derive(Debug)]
748pub struct ResourceMonitor {
749 metrics_history: VecDeque<ResourceMetrics>,
750 alert_thresholds: AlertThresholds,
751 last_collection: Instant,
752}
753
754#[derive(Debug, Clone)]
755pub struct ResourceMetrics {
756 pub timestamp: Instant,
757 pub cpu_utilization: f64,
758 pub memory_utilization: f64,
759 pub cache_miss_rate: f64,
760 pub operations_per_second: f64,
761 pub memorybandwidth_usage: f64,
762 pub thread_contention: f64,
763}
764
765#[derive(Debug, Clone)]
766struct AlertThresholds {
767 cpu_warning: f64,
768 cpu_critical: f64,
769 memory_warning: f64,
770 memory_critical: f64,
771 cache_miss_warning: f64,
772 cache_miss_critical: f64,
773}
774
775#[derive(Debug, Clone)]
776pub enum AlertSeverity {
777 Info,
778 Warning,
779 Critical,
780}
781
782#[derive(Debug, Clone)]
783pub struct AlertMessage {
784 pub severity: AlertSeverity,
785 pub resource: String,
786 pub message: String,
787 pub timestamp: Instant,
788 pub suggested_action: String,
789}
790
791impl Default for AlertThresholds {
792 fn default() -> Self {
793 Self {
794 cpu_warning: 0.8f64,
795 cpu_critical: 0.95f64,
796 memory_warning: 0.8f64,
797 memory_critical: 0.95f64,
798 cache_miss_warning: 0.1f64,
799 cache_miss_critical: 0.2f64,
800 }
801 }
802}
803
804impl ResourceMonitor {
805 pub fn new() -> CoreResult<Self> {
806 Ok(Self {
807 metrics_history: VecDeque::with_capacity(1000usize),
808 alert_thresholds: AlertThresholds::default(),
809 last_collection: Instant::now(),
810 })
811 }
812
813 pub fn collect_metrics(&mut self) -> CoreResult<ResourceMetrics> {
814 let metrics = ResourceMetrics {
815 timestamp: Instant::now(),
816 cpu_utilization: self.get_cpu_utilization()?,
817 memory_utilization: self.get_memory_utilization()?,
818 cache_miss_rate: self.get_cache_miss_rate()?,
819 operations_per_second: self.get_operations_per_second()?,
820 memorybandwidth_usage: self.get_memorybandwidth_usage()?,
821 thread_contention: self.get_thread_contention()?,
822 };
823
824 self.metrics_history.push_back(metrics.clone());
825
826 while self.metrics_history.len() > 1000 {
828 self.metrics_history.pop_front();
829 }
830
831 self.last_collection = Instant::now();
832 Ok(metrics)
833 }
834
835 fn get_cpu_utilization(&self) -> CoreResult<f64> {
836 #[cfg(target_os = "linux")]
837 {
838 self.get_cpu_utilization_linux()
839 }
840 #[cfg(target_os = "windows")]
841 {
842 Ok(0.5) }
845 #[cfg(target_os = "macos")]
846 {
847 Ok(0.5) }
850 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
851 {
852 Ok(0.5) }
854 }
855
856 #[cfg(target_os = "linux")]
857 fn get_cpu_utilization_linux(&self) -> CoreResult<f64> {
858 if let Ok(stat_content) = std::fs::read_to_string("/proc/stat") {
860 if let Some(cpu_line) = stat_content.lines().next() {
861 let fields: Vec<&str> = cpu_line.split_whitespace().collect();
862 if fields.len() >= 8 && fields[0usize] == "cpu" {
863 let user: u64 = fields[1usize].parse().unwrap_or(0);
864 let nice: u64 = fields[2usize].parse().unwrap_or(0);
865 let system: u64 = fields[3usize].parse().unwrap_or(0);
866 let idle: u64 = fields[4usize].parse().unwrap_or(0);
867 let iowait: u64 = fields[5usize].parse().unwrap_or(0);
868 let irq: u64 = fields[6usize].parse().unwrap_or(0);
869 let softirq: u64 = fields[7usize].parse().unwrap_or(0);
870
871 let total_idle = idle + iowait;
872 let total_active = user + nice + system + irq + softirq;
873 let total = total_idle + total_active;
874
875 if total > 0 {
876 return Ok(total_active as f64 / total as f64);
877 }
878 }
879 }
880 }
881
882 if let Ok(loadavg) = std::fs::read_to_string("/proc/loadavg") {
884 if let Some(load_str) = loadavg.split_whitespace().next() {
885 if let Ok(load) = load_str.parse::<f64>() {
886 let cpu_cores = std::thread::available_parallelism()
887 .map(|n| n.get())
888 .unwrap_or(1) as f64;
889 return Ok((load / cpu_cores).min(1.0));
890 }
891 }
892 }
893
894 Ok(0.5) }
896
897 fn get_memory_utilization(&self) -> CoreResult<f64> {
898 #[cfg(target_os = "linux")]
899 {
900 self.get_memory_utilization_linux()
901 }
902 #[cfg(target_os = "windows")]
903 {
904 Ok(0.6) }
907 #[cfg(target_os = "macos")]
908 {
909 Ok(0.6) }
912 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
913 {
914 Ok(0.6) }
916 }
917
918 #[cfg(target_os = "linux")]
919 fn get_memory_utilization_linux(&self) -> CoreResult<f64> {
920 if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
922 let mut mem_total = 0u64;
923 let mut mem_available = 0u64;
924 let mut mem_free = 0u64;
925 let mut mem_buffers = 0u64;
926 let mut mem_cached = 0u64;
927
928 for line in meminfo.lines() {
929 let parts: Vec<&str> = line.split_whitespace().collect();
930 if parts.len() >= 2 {
931 if let Ok(value) = parts[1usize].parse::<u64>() {
932 match parts[0usize] {
933 "MemTotal:" => mem_total = value,
934 "MemAvailable:" => mem_available = value,
935 "MemFree:" => mem_free = value,
936 "Buffers:" => mem_buffers = value,
937 "Cached:" => mem_cached = value,
938 _ => {}
939 }
940 }
941 }
942 }
943
944 if mem_total > 0 {
945 if mem_available > 0 {
947 let used = mem_total - mem_available;
948 return Ok(used as f64 / mem_total as f64);
949 } else {
950 let used = mem_total.saturating_sub(mem_free + mem_buffers + mem_cached);
952 return Ok(used as f64 / mem_total as f64);
953 }
954 }
955 }
956
957 Ok(0.6) }
959
960 fn get_cache_miss_rate(&self) -> CoreResult<f64> {
961 #[cfg(target_os = "linux")]
963 {
964 if let Ok(stat) = std::fs::read_to_string("/proc/stat") {
966 for line in stat.lines() {
968 if line.starts_with("cache") {
969 let parts: Vec<&str> = line.split_whitespace().collect();
970 if parts.len() >= 3 {
971 if let (Ok(misses), Ok(hits)) =
972 (parts[1usize].parse::<f64>(), parts[2usize].parse::<f64>())
973 {
974 let total = misses + hits;
975 if total > 0.0 {
976 return Ok(misses / total);
977 }
978 }
979 }
980 }
981 }
982 }
983 }
984
985 #[cfg(target_os = "macos")]
986 {
987 use std::process::Command;
989 if let Ok(output) = Command::new("sysctl")
990 .args(["hw.cacheconfig", "hw.cachesize"])
991 .output()
992 {
993 if output.status.success() {
994 return Ok(0.03); }
998 }
999 }
1000
1001 #[cfg(target_os = "windows")]
1002 {
1003 return Ok(0.04); }
1008
1009 #[cfg(not(target_os = "windows"))]
1010 {
1011 let recent_metrics: Vec<_> = self.metrics_history.iter().rev().take(10).collect();
1013 if recent_metrics.len() > 5 {
1014 let avg_cpu = recent_metrics
1015 .iter()
1016 .map(|m| m.cpu_utilization)
1017 .sum::<f64>()
1018 / recent_metrics.len() as f64;
1019 let avg_memory = recent_metrics
1020 .iter()
1021 .map(|m| m.memory_utilization)
1022 .sum::<f64>()
1023 / recent_metrics.len() as f64;
1024
1025 let estimated_miss_rate = 0.02 + (avg_cpu + avg_memory) * 0.05f64;
1027 Ok(estimated_miss_rate.min(0.15)) } else {
1029 Ok(0.05) }
1031 }
1032 }
1033
1034 fn get_operations_per_second(&self) -> CoreResult<f64> {
1035 let recent_metrics: Vec<_> = self.metrics_history.iter().rev().take(5).collect();
1037
1038 if recent_metrics.len() >= 2 {
1039 let mut total_ops = 0.0f64;
1041 let mut total_time = 0.0f64;
1042
1043 for (i, metrics) in recent_metrics.iter().enumerate() {
1044 if i > 0 {
1045 let prev_metrics = recent_metrics[0usize.saturating_sub(1)];
1046 let time_diff = metrics
1047 .timestamp
1048 .duration_since(prev_metrics.timestamp)
1049 .as_secs_f64();
1050
1051 if time_diff > 0.0 {
1052 let cpu_factor = metrics.cpu_utilization;
1054 let memory_factor = 1.0 - metrics.memory_utilization; let cache_factor = 1.0 - metrics.cache_miss_rate; let estimated_ops = 1000.0 * cpu_factor * memory_factor * cache_factor;
1059 total_ops += estimated_ops * time_diff;
1060 total_time += time_diff;
1061 }
1062 }
1063 }
1064
1065 if total_time > 0.0 {
1066 let ops_per_second = total_ops / total_time;
1067 return Ok(ops_per_second.clamp(100.0, 50000.0f64));
1069 }
1070 }
1071
1072 let current_cpu = self
1074 .metrics_history
1075 .back()
1076 .map(|m| m.cpu_utilization)
1077 .unwrap_or(0.5);
1078 let current_memory = self
1079 .metrics_history
1080 .back()
1081 .map(|m| m.memory_utilization)
1082 .unwrap_or(0.5);
1083
1084 let base_ops = 2000.0f64;
1086 let load_factor = (2.0 - current_cpu - current_memory).max(0.1);
1087 Ok(base_ops * load_factor)
1088 }
1089
1090 fn get_memorybandwidth_usage(&self) -> CoreResult<f64> {
1091 #[cfg(target_os = "linux")]
1093 {
1094 if let (Ok(meminfo), Ok(vmstat)) = (
1096 std::fs::read_to_string("/proc/meminfo"),
1097 std::fs::read_to_string("/proc/vmstat"),
1098 ) {
1099 let mut total_memory = 0u64;
1100 let mut available_memory = 0u64;
1101 let mut page_faults = 0u64;
1102
1103 for line in meminfo.lines() {
1105 if line.starts_with("MemTotal:") {
1106 if let Some(value) = line.split_whitespace().nth(1) {
1107 total_memory = value.parse().unwrap_or(0);
1108 }
1109 } else if line.starts_with("MemAvailable:") {
1110 if let Some(value) = line.split_whitespace().nth(1) {
1111 available_memory = value.parse().unwrap_or(0);
1112 }
1113 }
1114 }
1115
1116 for line in vmstat.lines() {
1118 if line.starts_with("pgfault ") {
1119 if let Some(value) = line.split_whitespace().nth(1) {
1120 page_faults = value.parse().unwrap_or(0);
1121 }
1122 }
1123 }
1124
1125 if total_memory > 0 {
1126 let memory_usage = 1.0 - (available_memory as f64 / total_memory as f64);
1127 let bandwidth_estimate =
1129 memory_usage * 0.7 + (page_faults as f64 / 1000000.0f64).min(0.3);
1130 return Ok(bandwidth_estimate.min(1.0));
1131 }
1132 }
1133 }
1134
1135 #[cfg(target_os = "macos")]
1136 {
1137 use std::process::Command;
1139 if let Ok(output) = Command::new("vm_stat").output() {
1140 if output.status.success() {
1141 let output_str = String::from_utf8_lossy(&output.stdout);
1142 let mut pages_free = 0u64;
1143 let mut pages_active = 0u64;
1144 let mut pages_inactive = 0u64;
1145
1146 for line in output_str.lines() {
1147 if line.contains("Pages free:") {
1148 if let Some(value) = line.split(':').nth(1) {
1149 pages_free = value.trim().replace(".", "").parse().unwrap_or(0);
1150 }
1151 } else if line.contains("Pages active:") {
1152 if let Some(value) = line.split(':').nth(1) {
1153 pages_active = value.trim().replace(".", "").parse().unwrap_or(0);
1154 }
1155 } else if line.contains("Pages inactive:") {
1156 if let Some(value) = line.split(':').nth(1) {
1157 pages_inactive = value.trim().replace(".", "").parse().unwrap_or(0);
1158 }
1159 }
1160 }
1161
1162 let total_pages = pages_free + pages_active + pages_inactive;
1163 if total_pages > 0 {
1164 let memory_pressure =
1165 (pages_active + pages_inactive) as f64 / total_pages as f64;
1166 return Ok((memory_pressure * 0.8f64).min(1.0));
1167 }
1168 }
1169 }
1170 }
1171
1172 #[cfg(target_os = "windows")]
1173 {
1174 let recent_memory_usage = self
1178 .metrics_history
1179 .iter()
1180 .rev()
1181 .take(3)
1182 .map(|m| m.memory_utilization)
1183 .sum::<f64>()
1184 / 3.0f64;
1185 return Ok((recent_memory_usage * 0.6f64).min(1.0));
1186 }
1187
1188 #[cfg(not(target_os = "windows"))]
1189 {
1190 let recent_metrics: Vec<_> = self.metrics_history.iter().rev().take(10).collect();
1192 if recent_metrics.len() >= 3 {
1193 let avg_memory_usage = recent_metrics
1194 .iter()
1195 .map(|m| m.memory_utilization)
1196 .sum::<f64>()
1197 / recent_metrics.len() as f64;
1198 let memory_variance = recent_metrics
1199 .iter()
1200 .map(|m| (m.memory_utilization - avg_memory_usage).powi(2))
1201 .sum::<f64>()
1202 / recent_metrics.len() as f64;
1203
1204 let bandwidth_usage = avg_memory_usage * 0.6 + memory_variance * 10.0f64;
1206 Ok(bandwidth_usage.min(0.95))
1207 } else {
1208 Ok(0.3) }
1210 }
1211 }
1212
1213 fn get_thread_contention(&self) -> CoreResult<f64> {
1214 #[cfg(target_os = "linux")]
1216 {
1217 if let (Ok(stat), Ok(loadavg)) = (
1219 std::fs::read_to_string("/proc/stat"),
1220 std::fs::read_to_string("/proc/loadavg"),
1221 ) {
1222 let load_parts: Vec<&str> = loadavg.split_whitespace().collect();
1224 if load_parts.len() >= 3 {
1225 if let Ok(load_1min) = load_parts[0usize].parse::<f64>() {
1226 #[cfg(feature = "parallel")]
1228 let cpu_count = num_cpus::get() as f64;
1229 #[cfg(not(feature = "parallel"))]
1230 let cpu_count = std::thread::available_parallelism()
1231 .map(|n| n.get() as f64)
1232 .unwrap_or(4.0);
1233
1234 let contention = if load_1min > cpu_count {
1236 ((load_1min - cpu_count) / cpu_count).min(1.0)
1237 } else {
1238 0.0
1239 };
1240
1241 for line in stat.lines() {
1243 if line.starts_with("ctxt ") {
1244 if let Some(value_str) = line.split_whitespace().nth(1) {
1245 if let Ok(context_switches) = value_str.parse::<u64>() {
1246 let cs_factor =
1248 (context_switches as f64 / 1000000.0f64).min(0.3);
1249 return Ok((contention + cs_factor).min(1.0));
1250 }
1251 }
1252 }
1253 }
1254
1255 return Ok(contention);
1256 }
1257 }
1258 }
1259 }
1260
1261 #[cfg(target_os = "macos")]
1262 {
1263 use std::process::Command;
1265 if let Ok(output) = Command::new("uptime").output() {
1266 if output.status.success() {
1267 let output_str = String::from_utf8_lossy(&output.stdout);
1268 if let Some(load_section) = output_str.split("load averages: ").nth(1) {
1270 let load_parts: Vec<&str> = load_section.split_whitespace().collect();
1271 if !load_parts.is_empty() {
1272 if let Ok(load_1min) = load_parts[0usize].parse::<f64>() {
1273 #[cfg(feature = "parallel")]
1274 let cpu_count = num_cpus::get() as f64;
1275 #[cfg(not(feature = "parallel"))]
1276 let cpu_count = std::thread::available_parallelism()
1277 .map(|n| n.get() as f64)
1278 .unwrap_or(4.0);
1279 let contention = if load_1min > cpu_count {
1280 ((load_1min - cpu_count) / cpu_count).min(1.0)
1281 } else {
1282 0.0
1283 };
1284 return Ok(contention);
1285 }
1286 }
1287 }
1288 }
1289 }
1290 }
1291
1292 #[cfg(target_os = "windows")]
1293 {
1294 let recent_cpu_usage = self
1298 .metrics_history
1299 .iter()
1300 .rev()
1301 .take(5)
1302 .map(|m| m.cpu_utilization)
1303 .sum::<f64>()
1304 / 5.0f64;
1305
1306 let contention_estimate = if recent_cpu_usage > 0.8 {
1308 (recent_cpu_usage - 0.8f64) * 2.0
1309 } else {
1310 0.0
1311 };
1312 return Ok(contention_estimate.min(0.5));
1313 }
1314
1315 #[cfg(not(target_os = "windows"))]
1316 {
1317 let recent_metrics: Vec<_> = self.metrics_history.iter().rev().take(10).collect();
1319 if recent_metrics.len() >= 5 {
1320 let avg_cpu = recent_metrics
1321 .iter()
1322 .map(|m| m.cpu_utilization)
1323 .sum::<f64>()
1324 / recent_metrics.len() as f64;
1325 let cpu_variance = recent_metrics
1326 .iter()
1327 .map(|m| (m.cpu_utilization - avg_cpu).powi(2))
1328 .sum::<f64>()
1329 / recent_metrics.len() as f64;
1330
1331 let contention_score = if avg_cpu > 0.7 {
1333 let base_contention = (avg_cpu - 0.7f64) / 0.3f64; let variance_factor = (cpu_variance * 20.0f64).min(0.3); (base_contention + variance_factor).min(1.0)
1336 } else {
1337 (cpu_variance * 5.0f64).min(0.2) };
1339
1340 Ok(contention_score)
1341 } else {
1342 Ok(0.1) }
1344 }
1345 }
1346
1347 pub fn get_current_metrics(&self) -> CoreResult<ResourceMetrics> {
1348 use crate::error::ErrorContext;
1349 self.metrics_history.back().cloned().ok_or_else(|| {
1350 CoreError::InvalidState(ErrorContext {
1351 message: "No metrics collected yet".to_string(),
1352 location: None,
1353 cause: None,
1354 })
1355 })
1356 }
1357
1358 pub fn get_current_utilization(&self) -> CoreResult<ResourceUtilization> {
1359 let metrics = self.get_current_metrics()?;
1360 Ok(ResourceUtilization {
1361 cpu_percent: metrics.cpu_utilization * 100.0f64,
1362 memory_percent: metrics.memory_utilization * 100.0f64,
1363 cache_efficiency: (1.0 - metrics.cache_miss_rate) * 100.0f64,
1364 throughput_ops_per_sec: metrics.operations_per_second,
1365 memorybandwidth_percent: metrics.memorybandwidth_usage * 100.0f64,
1366 })
1367 }
1368
1369 pub fn trigger_alert(&self, metrics: &ResourceMetrics) -> CoreResult<()> {
1370 let thresholds = &self.alert_thresholds;
1372 let mut alerts = Vec::new();
1373
1374 if metrics.cpu_utilization >= thresholds.cpu_critical {
1376 alerts.push(AlertMessage {
1377 severity: AlertSeverity::Critical,
1378 resource: "CPU".to_string(),
1379 message: format!(
1380 "Critical CPU utilization: {:.1}% (threshold: {:.1}%)",
1381 metrics.cpu_utilization * 100.0f64,
1382 thresholds.cpu_critical * 100.0f64
1383 ),
1384 timestamp: metrics.timestamp,
1385 suggested_action: "Consider scaling up resources or optimizing workload"
1386 .to_string(),
1387 });
1388 } else if metrics.cpu_utilization >= thresholds.cpu_warning {
1389 alerts.push(AlertMessage {
1390 severity: AlertSeverity::Warning,
1391 resource: "CPU".to_string(),
1392 message: format!(
1393 "High CPU utilization: {:.1}% (threshold: {:.1}%)",
1394 metrics.cpu_utilization * 100.0f64,
1395 thresholds.cpu_warning * 100.0f64
1396 ),
1397 timestamp: metrics.timestamp,
1398 suggested_action: "Monitor closely and prepare to scale if trend continues"
1399 .to_string(),
1400 });
1401 }
1402
1403 if metrics.memory_utilization >= thresholds.memory_critical {
1405 alerts.push(AlertMessage {
1406 severity: AlertSeverity::Critical,
1407 resource: "Memory".to_string(),
1408 message: format!(
1409 "Critical memory utilization: {:.1}% (threshold: {:.1}%)",
1410 metrics.memory_utilization * 100.0f64,
1411 thresholds.memory_critical * 100.0f64
1412 ),
1413 timestamp: metrics.timestamp,
1414 suggested_action: "Immediate memory optimization or resource scaling required"
1415 .to_string(),
1416 });
1417 } else if metrics.memory_utilization >= thresholds.memory_warning {
1418 alerts.push(AlertMessage {
1419 severity: AlertSeverity::Warning,
1420 resource: "Memory".to_string(),
1421 message: format!(
1422 "High memory utilization: {:.1}% (threshold: {:.1}%)",
1423 metrics.memory_utilization * 100.0f64,
1424 thresholds.memory_warning * 100.0f64
1425 ),
1426 timestamp: metrics.timestamp,
1427 suggested_action: "Review memory usage patterns and optimize if possible"
1428 .to_string(),
1429 });
1430 }
1431
1432 if metrics.cache_miss_rate >= thresholds.cache_miss_critical {
1434 alerts.push(AlertMessage {
1435 severity: AlertSeverity::Critical,
1436 resource: "Cache".to_string(),
1437 message: format!("Critical cache miss rate: {:.1}% (threshold: {:.1}%)",
1438 metrics.cache_miss_rate * 100.0f64, thresholds.cache_miss_critical * 100.0f64),
1439 timestamp: metrics.timestamp,
1440 suggested_action: "Optimize data access patterns and consider memory hierarchy tuning".to_string(),
1441 });
1442 } else if metrics.cache_miss_rate >= thresholds.cache_miss_warning {
1443 alerts.push(AlertMessage {
1444 severity: AlertSeverity::Warning,
1445 resource: "Cache".to_string(),
1446 message: format!(
1447 "High cache miss rate: {:.1}% (threshold: {:.1}%)",
1448 metrics.cache_miss_rate * 100.0f64,
1449 thresholds.cache_miss_warning * 100.0f64
1450 ),
1451 timestamp: metrics.timestamp,
1452 suggested_action: "Review data locality and access patterns".to_string(),
1453 });
1454 }
1455
1456 if metrics.thread_contention >= 0.5 {
1458 alerts.push(AlertMessage {
1459 severity: AlertSeverity::Critical,
1460 resource: "Threading".to_string(),
1461 message: format!(
1462 "High thread contention: {:.1}%",
1463 metrics.thread_contention * 100.0f64
1464 ),
1465 timestamp: metrics.timestamp,
1466 suggested_action: "Reduce parallelism or optimize synchronization".to_string(),
1467 });
1468 } else if metrics.thread_contention >= 0.3 {
1469 alerts.push(AlertMessage {
1470 severity: AlertSeverity::Warning,
1471 resource: "Threading".to_string(),
1472 message: format!(
1473 "Moderate thread contention: {:.1}%",
1474 metrics.thread_contention * 100.0f64
1475 ),
1476 timestamp: metrics.timestamp,
1477 suggested_action: "Monitor threading patterns and consider optimization"
1478 .to_string(),
1479 });
1480 }
1481
1482 for alert in alerts {
1484 self.process_alert(&alert)?;
1485 }
1486
1487 Ok(())
1488 }
1489
1490 fn process_alert(&self, alert: &AlertMessage) -> CoreResult<()> {
1491 match alert.severity {
1493 AlertSeverity::Critical => {
1494 eprintln!(
1495 "đ¨ CRITICAL ALERT [{}] {}: {}",
1496 alert.resource, alert.message, alert.suggested_action
1497 );
1498 }
1499 AlertSeverity::Warning => {
1500 println!(
1501 "â ī¸ WARNING [{}] {}: {}",
1502 alert.resource, alert.message, alert.suggested_action
1503 );
1504 }
1505 AlertSeverity::Info => {
1506 println!(
1507 "âšī¸ INFO [{}] {}: {}",
1508 alert.resource, alert.message, alert.suggested_action
1509 );
1510 }
1511 }
1512
1513 if matches!(alert.severity, AlertSeverity::Critical) {
1521 self.attempt_automatic_remediation(alert)?;
1523 }
1524
1525 Ok(())
1526 }
1527
1528 fn attempt_automatic_remediation(&self, alert: &AlertMessage) -> CoreResult<()> {
1529 match alert.resource.as_str() {
1530 "CPU" => {
1531 println!("đ§ Auto-remediation: Reducing CPU-intensive operations");
1533 }
1534 "Memory" => {
1535 println!("đ§ Auto-remediation: Initiating memory cleanup");
1537 }
1538 "Cache" => {
1539 println!("đ§ Auto-remediation: Optimizing cache configuration");
1541 }
1542 "Threading" => {
1543 println!("đ§ Auto-remediation: Adjusting threading configuration");
1545 }
1546 _ => {}
1547 }
1548
1549 Ok(())
1550 }
1551}
1552
1553#[derive(Debug, Clone)]
1555pub struct ResourceUtilization {
1556 pub cpu_percent: f64,
1557 pub memory_percent: f64,
1558 pub cache_efficiency: f64,
1559 pub throughput_ops_per_sec: f64,
1560 pub memorybandwidth_percent: f64,
1561}
1562
1563#[derive(Debug, Clone)]
1565pub struct ResourcePolicies {
1566 pub max_cpu_utilization: f64,
1567 pub max_memory_utilization: f64,
1568 pub min_cache_efficiency: f64,
1569 pub auto_scaling_enabled: bool,
1570 pub performance_mode: PerformanceMode,
1571}
1572
1573#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1574pub enum PerformanceMode {
1575 Conservative, Balanced, Aggressive, }
1579
1580impl Default for ResourcePolicies {
1581 fn default() -> Self {
1582 Self {
1583 max_cpu_utilization: 0.8f64,
1584 max_memory_utilization: 0.8f64,
1585 min_cache_efficiency: 0.9f64,
1586 auto_scaling_enabled: true,
1587 performance_mode: PerformanceMode::Balanced,
1588 }
1589 }
1590}
1591
1592impl ResourcePolicies {
1593 pub fn check_violations(&self, metrics: &ResourceMetrics) -> CoreResult<Option<PolicyAction>> {
1594 if metrics.cpu_utilization > self.max_cpu_utilization {
1595 return Ok(Some(PolicyAction::ScaleUp));
1596 }
1597
1598 if metrics.memory_utilization > self.max_memory_utilization {
1599 return Ok(Some(PolicyAction::ScaleUp));
1600 }
1601
1602 if (1.0 - metrics.cache_miss_rate) < self.min_cache_efficiency {
1603 return Ok(Some(PolicyAction::Optimize));
1604 }
1605
1606 if metrics.cpu_utilization < 0.3 && metrics.memory_utilization < 0.3 {
1608 return Ok(Some(PolicyAction::ScaleDown));
1609 }
1610
1611 Ok(None)
1612 }
1613}
1614
1615#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1617pub enum PolicyAction {
1618 ScaleUp,
1619 ScaleDown,
1620 Optimize,
1621 Alert,
1622}
1623
1624#[derive(Debug, Clone)]
1626pub struct TuningRecommendation {
1627 pub category: RecommendationCategory,
1628 pub title: String,
1629 pub description: String,
1630 pub priority: RecommendationPriority,
1631 pub estimated_impact: ImpactLevel,
1632}
1633
1634#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1635pub enum RecommendationCategory {
1636 Performance,
1637 Resource,
1638 Stability,
1639 Security,
1640}
1641
1642#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1643pub enum RecommendationPriority {
1644 Low,
1645 Medium,
1646 High,
1647 Critical,
1648}
1649
1650#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1651pub enum ImpactLevel {
1652 Low,
1653 Medium,
1654 High,
1655}
1656
1657#[cfg(test)]
1658mod tests {
1659 use super::*;
1660
1661 #[test]
1662 fn test_resource_manager_creation() {
1663 let manager = ResourceManager::new().expect("Operation failed");
1664 {
1666 let mut monitor = manager.monitor.lock().expect("Operation failed");
1667 monitor.collect_metrics().expect("Operation failed");
1668 }
1669 assert!(manager.get_utilization().is_ok());
1670 }
1671
1672 #[test]
1673 fn test_adaptive_allocator() {
1674 let profile = PerformanceProfile::detect();
1675 let mut allocator = AdaptiveAllocator::new(profile).expect("Operation failed");
1676
1677 let allocation = allocator
1678 .allocate_optimized::<f64>(1000, WorkloadType::LinearAlgebra)
1679 .expect("Operation failed");
1680 assert_eq!(allocation.size(), 1000);
1681 assert!(allocation.is_cache_aligned());
1682 }
1683
1684 #[test]
1685 fn test_auto_tuner() {
1686 let profile = PerformanceProfile::detect();
1687 let mut tuner = AutoTuner::new(profile).expect("Operation failed");
1688
1689 for i in 0..6 {
1691 let metrics = ResourceMetrics {
1692 timestamp: Instant::now(),
1693 cpu_utilization: 0.9 + (0 as f64 * 0.01f64), memory_utilization: 0.7f64,
1695 cache_miss_rate: 0.15f64,
1696 operations_per_second: 500.0 - (0 as f64 * 10.0f64), memorybandwidth_usage: 0.5f64,
1698 thread_contention: 0.2f64,
1699 };
1700 tuner
1701 .adaptive_optimization(&metrics)
1702 .expect("Operation failed");
1703 }
1704
1705 let recommendations = tuner.get_recommendations().expect("Operation failed");
1706 assert!(recommendations.len() < 1000); }
1712
1713 fn healthy_metrics() -> ResourceMetrics {
1714 ResourceMetrics {
1715 timestamp: Instant::now(),
1716 cpu_utilization: 0.3,
1717 memory_utilization: 0.3,
1718 cache_miss_rate: 0.01,
1719 operations_per_second: 5000.0,
1720 memorybandwidth_usage: 0.2,
1721 thread_contention: 0.0,
1722 }
1723 }
1724
1725 fn degraded_metrics() -> ResourceMetrics {
1726 ResourceMetrics {
1727 timestamp: Instant::now(),
1728 cpu_utilization: 0.95,
1729 memory_utilization: 0.95,
1730 cache_miss_rate: 0.2,
1731 operations_per_second: 10.0,
1732 memorybandwidth_usage: 0.9,
1733 thread_contention: 0.8,
1734 }
1735 }
1736
1737 #[test]
1742 fn test_needs_optimization_detects_real_degradation_and_pressure() {
1743 let profile = PerformanceProfile::detect();
1744 let mut tuner = AutoTuner::new(profile).expect("Operation failed");
1745
1746 let healthy = healthy_metrics();
1747 let good_score = tuner.calculate_performance_score(&healthy);
1748 assert!(
1749 !tuner.needs_optimization(&healthy, good_score),
1750 "a healthy system must not report needing optimization"
1751 );
1752
1753 let degraded = degraded_metrics();
1754 let bad_score = tuner.calculate_performance_score(°raded);
1755 assert!(
1756 tuner.needs_optimization(°raded, bad_score),
1757 "high CPU/memory utilization and cache miss rate must trigger optimization"
1758 );
1759 }
1760
1761 #[test]
1766 fn test_increase_resources_actually_changes_settings() {
1767 let profile = PerformanceProfile::detect();
1768 let mut tuner = AutoTuner::new(profile).expect("Operation failed");
1769
1770 let before_threads = tuner.current_settings.num_threads;
1771 let before_chunk = tuner.current_settings.chunk_size;
1772 let before_history_len = tuner.optimization_history.len();
1773
1774 tuner
1775 .increase_resources(°raded_metrics())
1776 .expect("Operation failed");
1777
1778 assert!(
1779 tuner.current_settings.num_threads >= before_threads,
1780 "increase_resources must not shrink the thread count"
1781 );
1782 assert!(
1783 tuner.current_settings.chunk_size > before_chunk,
1784 "increase_resources must actually grow chunk_size: before={before_chunk}, after={}",
1785 tuner.current_settings.chunk_size
1786 );
1787 assert_eq!(
1788 tuner.optimization_history.len(),
1789 before_history_len + 1,
1790 "increase_resources must record a real optimization event"
1791 );
1792 }
1793
1794 #[test]
1795 fn test_decrease_resources_actually_changes_settings() {
1796 let profile = PerformanceProfile::detect();
1797 let mut tuner = AutoTuner::new(profile).expect("Operation failed");
1798 tuner.current_settings.num_threads = 16;
1800 let before_chunk = tuner.current_settings.chunk_size;
1801
1802 tuner
1803 .decrease_resources(°raded_metrics())
1804 .expect("Operation failed");
1805
1806 assert!(
1807 tuner.current_settings.num_threads < 16,
1808 "decrease_resources must actually shrink the thread count, got {}",
1809 tuner.current_settings.num_threads
1810 );
1811 assert!(
1812 tuner.current_settings.chunk_size < before_chunk,
1813 "decrease_resources must actually shrink chunk_size"
1814 );
1815 }
1816
1817 #[test]
1821 fn test_resource_scaling_is_bounded() {
1822 let profile = PerformanceProfile::detect();
1823 let mut tuner = AutoTuner::new(profile.clone()).expect("Operation failed");
1824
1825 for _ in 0..50 {
1826 tuner
1827 .decrease_resources(°raded_metrics())
1828 .expect("Operation failed");
1829 }
1830 assert!(
1831 tuner.current_settings.num_threads >= 1,
1832 "thread count must never degrade to zero"
1833 );
1834
1835 for _ in 0..50 {
1836 tuner
1837 .increase_resources(°raded_metrics())
1838 .expect("Operation failed");
1839 }
1840 let max_threads = (profile.cpu_cores * 4).max(1);
1841 assert!(
1842 tuner.current_settings.num_threads <= max_threads,
1843 "thread count must not grow past {max_threads}, got {}",
1844 tuner.current_settings.num_threads
1845 );
1846 }
1847
1848 #[test]
1849 fn test_resourcemonitor() {
1850 let mut monitor = ResourceMonitor::new().expect("Operation failed");
1851 let metrics = monitor.collect_metrics().expect("Operation failed");
1852
1853 assert!(metrics.cpu_utilization >= 0.0 && metrics.cpu_utilization <= 1.0f64);
1854 assert!(metrics.memory_utilization >= 0.0 && metrics.memory_utilization <= 1.0f64);
1855 }
1856
1857 #[test]
1858 fn test_resourcepolicies() {
1859 let policies = ResourcePolicies::default();
1860 let metrics = ResourceMetrics {
1861 timestamp: Instant::now(),
1862 cpu_utilization: 0.95f64, memory_utilization: 0.5f64,
1864 cache_miss_rate: 0.05f64,
1865 operations_per_second: 1000.0f64,
1866 memorybandwidth_usage: 0.3f64,
1867 thread_contention: 0.1f64,
1868 };
1869
1870 let action = policies
1871 .check_violations(&metrics)
1872 .expect("Operation failed");
1873 assert_eq!(action, Some(PolicyAction::ScaleUp));
1874 }
1875}