1use std::collections::{HashMap, VecDeque};
40use std::sync::{Arc, Mutex, OnceLock};
41use std::time::{Duration, Instant};
42
43static GLOBAL_MONITOR: OnceLock<Arc<Mutex<GlobalMemoryMonitor>>> = OnceLock::new();
45
46#[derive(Debug)]
48struct GlobalMemoryMonitor {
49 monitors: HashMap<String, Arc<Mutex<MemoryMonitor>>>,
51
52 global_stats: GlobalMemoryStats,
54
55 enabled: bool,
57
58 max_monitors: usize,
60}
61
62#[derive(Debug, Clone)]
64pub struct GlobalMemoryStats {
65 pub total_allocated_bytes: usize,
67
68 pub peak_total_bytes: usize,
70
71 pub active_interpolators: usize,
73
74 pub total_allocations: u64,
76
77 pub total_deallocations: u64,
79
80 pub monitoring_start: Instant,
82}
83
84impl Default for GlobalMemoryStats {
85 fn default() -> Self {
86 Self {
87 total_allocated_bytes: 0,
88 peak_total_bytes: 0,
89 active_interpolators: 0,
90 total_allocations: 0,
91 total_deallocations: 0,
92 monitoring_start: Instant::now(),
93 }
94 }
95}
96
97#[derive(Debug)]
99pub struct MemoryMonitor {
100 name: String,
102
103 allocations: HashMap<String, usize>,
105
106 allocation_history: VecDeque<AllocationEvent>,
108
109 peak_memory_bytes: usize,
111
112 current_memory_bytes: usize,
114
115 leak_stats: LeakDetectionStats,
117
118 perf_metrics: MemoryPerformanceMetrics,
120
121 active: bool,
123
124 created_at: Instant,
126}
127
128#[derive(Debug, Clone)]
130struct AllocationEvent {
131 event_type: EventType,
133
134 sizebytes: usize,
136
137 #[allow(dead_code)]
139 category: String,
140
141 #[allow(dead_code)]
143 timestamp: Instant,
144}
145
146#[derive(Debug, Clone, Copy, PartialEq)]
148enum EventType {
149 Allocation,
150 Deallocation,
151}
152
153#[derive(Debug, Clone)]
155struct LeakDetectionStats {
156 total_allocations: u64,
158
159 total_deallocations: u64,
161
162 #[allow(dead_code)]
164 unmatched_allocations: u64,
165
166 long_lived_allocations: HashMap<String, (usize, Instant)>,
168
169 leak_detection_threshold: Duration,
171}
172
173impl Default for LeakDetectionStats {
174 fn default() -> Self {
175 Self {
176 total_allocations: 0,
177 total_deallocations: 0,
178 unmatched_allocations: 0,
179 long_lived_allocations: HashMap::new(),
180 leak_detection_threshold: Duration::from_secs(300), }
182 }
183}
184
185#[derive(Debug, Clone)]
187struct MemoryPerformanceMetrics {
188 avg_allocation_size: f64,
190
191 #[allow(dead_code)]
193 avg_allocation_interval: Duration,
194
195 #[allow(dead_code)]
197 fragmentation_estimate: f64,
198
199 cache_hit_ratio: f64,
201
202 last_update: Instant,
204}
205
206impl Default for MemoryPerformanceMetrics {
207 fn default() -> Self {
208 Self {
209 avg_allocation_size: 0.0,
210 avg_allocation_interval: Duration::from_millis(0),
211 fragmentation_estimate: 0.0,
212 cache_hit_ratio: 0.0,
213 last_update: Instant::now(),
214 }
215 }
216}
217
218#[derive(Debug, Clone)]
220pub struct MemoryReport {
221 pub monitorname: String,
223
224 pub current_allocations: HashMap<String, usize>,
226
227 pub peak_memory_bytes: usize,
229
230 pub total_allocated_bytes: usize,
232
233 pub leak_indicators: LeakIndicators,
235
236 pub performance_summary: PerformanceSummary,
238
239 pub recommendations: Vec<String>,
241
242 pub generated_at: Instant,
244}
245
246#[derive(Debug, Clone)]
248pub struct LeakIndicators {
249 pub has_potential_leaks: bool,
251
252 pub unmatched_allocations: u64,
254
255 pub long_lived_memory_bytes: usize,
257
258 pub suspicious_categories: Vec<String>,
260
261 pub leak_severity: f64,
263}
264
265#[derive(Debug, Clone)]
267pub struct PerformanceSummary {
268 pub memory_efficiency_score: f64,
270
271 pub allocation_pattern_score: f64,
273
274 pub cache_utilization_score: f64,
276
277 pub overall_grade: PerformanceGrade,
279}
280
281#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
283pub enum PerformanceGrade {
284 Excellent,
285 Good,
286 Fair,
287 Poor,
288 Critical,
289}
290
291impl MemoryMonitor {
292 pub fn new(name: impl Into<String>) -> Self {
294 let name = name.into();
295 let monitor = Self {
296 name: name.clone(),
297 allocations: HashMap::new(),
298 allocation_history: VecDeque::new(),
299 peak_memory_bytes: 0,
300 current_memory_bytes: 0,
301 leak_stats: LeakDetectionStats::default(),
302 perf_metrics: MemoryPerformanceMetrics::default(),
303 active: true,
304 created_at: Instant::now(),
305 };
306
307 register_monitor(&name, monitor.clone());
309 monitor
310 }
311
312 pub fn track_allocation(&mut self, sizebytes: usize, category: impl Into<String>) {
314 if !self.active {
315 return;
316 }
317
318 let category = category.into();
319 let now = Instant::now();
320
321 *self.allocations.entry(category.clone()).or_insert(0) += sizebytes;
323 self.current_memory_bytes += sizebytes;
324
325 if self.current_memory_bytes > self.peak_memory_bytes {
327 self.peak_memory_bytes = self.current_memory_bytes;
328 }
329
330 let event = AllocationEvent {
332 event_type: EventType::Allocation,
333 sizebytes,
334 category: category.clone(),
335 timestamp: now,
336 };
337
338 self.allocation_history.push_back(event);
339
340 if self.allocation_history.len() > 10000 {
342 self.allocation_history.pop_front();
343 }
344
345 self.leak_stats.total_allocations += 1;
347 self.leak_stats.long_lived_allocations.insert(
348 format!("{}_{}", category, self.leak_stats.total_allocations),
349 (sizebytes, now),
350 );
351
352 self.update_performance_metrics();
354
355 update_global_stats(sizebytes, true);
357 }
358
359 pub fn track_deallocation(&mut self, sizebytes: usize, category: impl Into<String>) {
361 if !self.active {
362 return;
363 }
364
365 let category = category.into();
366 let now = Instant::now();
367
368 if let Some(current) = self.allocations.get_mut(&category) {
370 *current = current.saturating_sub(sizebytes);
371 if *current == 0 {
372 self.allocations.remove(&category);
373 }
374 }
375
376 self.current_memory_bytes = self.current_memory_bytes.saturating_sub(sizebytes);
377
378 let event = AllocationEvent {
380 event_type: EventType::Deallocation,
381 sizebytes,
382 category: category.clone(),
383 timestamp: now,
384 };
385
386 self.allocation_history.push_back(event);
387
388 self.leak_stats.total_deallocations += 1;
390
391 self.leak_stats
393 .long_lived_allocations
394 .retain(|k, _| !k.starts_with(&category));
395
396 self.update_performance_metrics();
398
399 update_global_stats(sizebytes, false);
401 }
402
403 pub fn generate_report(&self) -> MemoryReport {
405 let leak_indicators = self.analyze_leaks();
406 let performance_summary = self.analyze_performance();
407 let recommendations = self.generate_recommendations(&leak_indicators, &performance_summary);
408
409 MemoryReport {
410 monitorname: self.name.clone(),
411 current_allocations: self.allocations.clone(),
412 peak_memory_bytes: self.peak_memory_bytes,
413 total_allocated_bytes: self.calculate_total_allocated(),
414 leak_indicators,
415 performance_summary,
416 recommendations,
417 generated_at: Instant::now(),
418 }
419 }
420
421 fn analyze_leaks(&self) -> LeakIndicators {
423 let unmatched = self
424 .leak_stats
425 .total_allocations
426 .saturating_sub(self.leak_stats.total_deallocations);
427
428 let now = Instant::now();
430 let long_lived_memory: usize = self
431 .leak_stats
432 .long_lived_allocations
433 .values()
434 .filter(|(_, timestamp)| {
435 now.duration_since(*timestamp) > self.leak_stats.leak_detection_threshold
436 })
437 .map(|(size, _)| *size)
438 .sum();
439
440 let suspicious_categories: Vec<String> = self.allocations
442 .iter()
443 .filter(|(_, &size)| size > 1024 * 1024) .map(|(cat, _)| cat.clone())
445 .collect();
446
447 let has_potential_leaks =
448 unmatched > 0 || long_lived_memory > 0 || !suspicious_categories.is_empty();
449
450 let leak_severity = if has_potential_leaks {
452 let severity_factors = [
453 (unmatched as f64) / (self.leak_stats.total_allocations as f64).max(1.0),
454 (long_lived_memory as f64) / (self.peak_memory_bytes as f64).max(1.0),
455 (suspicious_categories.len() as f64) / 10.0, ];
457 severity_factors.iter().sum::<f64>() / severity_factors.len() as f64
458 } else {
459 0.0
460 };
461
462 LeakIndicators {
463 has_potential_leaks,
464 unmatched_allocations: unmatched,
465 long_lived_memory_bytes: long_lived_memory,
466 suspicious_categories,
467 leak_severity: leak_severity.min(1.0),
468 }
469 }
470
471 fn analyze_performance(&self) -> PerformanceSummary {
473 let memory_efficiency_score = if self.peak_memory_bytes > 0 {
475 1.0 - (self.current_memory_bytes as f64 / self.peak_memory_bytes as f64)
476 } else {
477 1.0
478 };
479
480 let allocation_pattern_score = if self.leak_stats.total_allocations > 0 {
482 let deallocation_ratio = self.leak_stats.total_deallocations as f64
483 / self.leak_stats.total_allocations as f64;
484 deallocation_ratio.min(1.0)
485 } else {
486 1.0
487 };
488
489 let cache_utilization_score = self.perf_metrics.cache_hit_ratio;
491
492 let overall_score =
494 (memory_efficiency_score + allocation_pattern_score + cache_utilization_score) / 3.0;
495 let overall_grade = match overall_score {
496 s if s >= 0.9 => PerformanceGrade::Excellent,
497 s if s >= 0.7 => PerformanceGrade::Good,
498 s if s >= 0.5 => PerformanceGrade::Fair,
499 s if s >= 0.3 => PerformanceGrade::Poor,
500 _ => PerformanceGrade::Critical,
501 };
502
503 PerformanceSummary {
504 memory_efficiency_score,
505 allocation_pattern_score,
506 cache_utilization_score,
507 overall_grade,
508 }
509 }
510
511 fn generate_recommendations(
513 &self,
514 leak_indicators: &LeakIndicators,
515 performance: &PerformanceSummary,
516 ) -> Vec<String> {
517 let mut recommendations = Vec::new();
518
519 if leak_indicators.has_potential_leaks {
520 recommendations
521 .push("Consider implementing explicit memory cleanup in destructor".to_string());
522
523 if leak_indicators.unmatched_allocations > 0 {
524 recommendations.push(format!(
525 "Found {} unmatched allocations - check for missing deallocations",
526 leak_indicators.unmatched_allocations
527 ));
528 }
529
530 if leak_indicators.long_lived_memory_bytes > 1024 * 1024 {
531 recommendations.push(format!(
532 "Large amount of long-lived memory ({} MB) - consider periodic cleanup",
533 leak_indicators.long_lived_memory_bytes / (1024 * 1024)
534 ));
535 }
536 }
537
538 if matches!(
539 performance.overall_grade,
540 PerformanceGrade::Fair | PerformanceGrade::Poor | PerformanceGrade::Critical
541 ) {
542 recommendations.push("Memory performance can be improved".to_string());
543
544 if performance.memory_efficiency_score < 0.5 {
545 recommendations.push(
546 "High peak memory usage - consider processing data in chunks".to_string(),
547 );
548 }
549
550 if performance.cache_utilization_score < 0.3 {
551 recommendations.push(
552 "Low cache utilization - enable caching for repeated operations".to_string(),
553 );
554 }
555 }
556
557 #[cfg(target_pointer_width = "32")]
558 let high_memory_threshold = 256 * 1024 * 1024; #[cfg(target_pointer_width = "64")]
560 let high_memory_threshold = 1024 * 1024 * 1024; if self.peak_memory_bytes > high_memory_threshold {
563 recommendations.push(
564 "Very high memory usage - consider using memory-efficient algorithms".to_string(),
565 );
566 }
567
568 recommendations
569 }
570
571 fn update_performance_metrics(&mut self) {
573 let now = Instant::now();
574
575 if self.leak_stats.total_allocations > 0 {
577 let total_size: usize = self
578 .allocation_history
579 .iter()
580 .filter(|e| e.event_type == EventType::Allocation)
581 .map(|e| e.sizebytes)
582 .sum();
583 self.perf_metrics.avg_allocation_size =
584 total_size as f64 / self.leak_stats.total_allocations as f64;
585 }
586
587 self.perf_metrics.cache_hit_ratio = 0.7; self.perf_metrics.last_update = now;
591 }
592
593 fn calculate_total_allocated(&self) -> usize {
595 self.allocation_history
596 .iter()
597 .filter(|e| e.event_type == EventType::Allocation)
598 .map(|e| e.sizebytes)
599 .sum()
600 }
601
602 pub fn disable(&mut self) {
604 self.active = false;
605 }
606
607 pub fn is_active(&self) -> bool {
609 self.active
610 }
611}
612
613impl Clone for MemoryMonitor {
614 fn clone(&self) -> Self {
615 Self {
616 name: format!("{}_clone", self.name),
617 allocations: self.allocations.clone(),
618 allocation_history: self.allocation_history.clone(),
619 peak_memory_bytes: self.peak_memory_bytes,
620 current_memory_bytes: self.current_memory_bytes,
621 leak_stats: self.leak_stats.clone(),
622 perf_metrics: self.perf_metrics.clone(),
623 active: self.active,
624 created_at: self.created_at,
625 }
626 }
627}
628
629impl MemoryReport {
630 pub fn has_potential_leaks(&self) -> bool {
632 self.leak_indicators.has_potential_leaks
633 }
634
635 pub fn memory_efficiency_rating(&self) -> PerformanceGrade {
637 self.performance_summary.overall_grade
638 }
639
640 pub fn summary(&self) -> String {
642 format!(
643 "Memory Report for '{}': Current: {} KB, Peak: {} KB, Grade: {:?}, Leaks: {}",
644 self.monitorname,
645 self.current_allocations.values().sum::<usize>() / 1024,
646 self.peak_memory_bytes / 1024,
647 self.performance_summary.overall_grade,
648 if self.has_potential_leaks() {
649 "Detected"
650 } else {
651 "None"
652 }
653 )
654 }
655}
656
657#[allow(dead_code)]
660pub fn start_monitoring() {
661 let _ = GLOBAL_MONITOR.set(Arc::new(Mutex::new(GlobalMemoryMonitor {
662 monitors: HashMap::new(),
663 global_stats: GlobalMemoryStats::default(),
664 enabled: true,
665 max_monitors: 100,
666 })));
667}
668
669#[allow(dead_code)]
671pub fn stop_monitoring() {
672 if let Some(monitor) = GLOBAL_MONITOR.get() {
673 if let Ok(mut global) = monitor.lock() {
674 global.enabled = false;
675 global.monitors.clear();
676 }
677 }
678}
679
680#[allow(dead_code)]
682fn register_monitor(name: &str, monitor: MemoryMonitor) {
683 if let Some(global_monitor) = GLOBAL_MONITOR.get() {
684 if let Ok(mut global) = global_monitor.lock() {
685 if global.enabled && global.monitors.len() < global.max_monitors {
686 global
687 .monitors
688 .insert(name.to_string(), Arc::new(Mutex::new(monitor)));
689 global.global_stats.active_interpolators = global.monitors.len();
690 }
691 }
692 }
693}
694
695#[allow(dead_code)]
697fn update_global_stats(sizebytes: usize, isallocation: bool) {
698 if let Some(global_monitor) = GLOBAL_MONITOR.get() {
699 if let Ok(mut global) = global_monitor.lock() {
700 if isallocation {
701 global.global_stats.total_allocated_bytes += sizebytes;
702 global.global_stats.total_allocations += 1;
703
704 if global.global_stats.total_allocated_bytes > global.global_stats.peak_total_bytes
705 {
706 global.global_stats.peak_total_bytes =
707 global.global_stats.total_allocated_bytes;
708 }
709 } else {
710 global.global_stats.total_allocated_bytes = global
711 .global_stats
712 .total_allocated_bytes
713 .saturating_sub(sizebytes);
714 global.global_stats.total_deallocations += 1;
715 }
716 }
717 }
718}
719
720#[allow(dead_code)]
722pub fn get_global_stats() -> Option<GlobalMemoryStats> {
723 GLOBAL_MONITOR
724 .get()
725 .and_then(|monitor| monitor.lock().ok())
726 .map(|global| global.global_stats.clone())
727}
728
729#[allow(dead_code)]
731pub fn get_monitor_report(name: &str) -> Option<MemoryReport> {
732 GLOBAL_MONITOR
733 .get()
734 .and_then(|global_monitor| {
735 global_monitor
736 .lock()
737 .ok()
738 .and_then(|global| global.monitors.get(name).cloned())
739 })
740 .and_then(|monitor| monitor.lock().ok().map(|m| m.generate_report()))
741}
742
743#[allow(dead_code)]
745pub fn get_all_reports() -> Vec<MemoryReport> {
746 if let Some(global_monitor) = GLOBAL_MONITOR.get() {
747 if let Ok(global) = global_monitor.lock() {
748 return global
749 .monitors
750 .values()
751 .filter_map(|monitor| monitor.lock().ok())
752 .map(|m| m.generate_report())
753 .collect();
754 }
755 }
756 Vec::new()
757}
758
759#[derive(Debug)]
761pub struct StressMemoryProfiler {
762 base_monitor: MemoryMonitor,
764
765 stress_metrics: StressMemoryMetrics,
767
768 stress_history: VecDeque<MemorySnapshot>,
770
771 pressure_indicators: MemoryPressureIndicators,
773
774 stress_config: StressProfilingConfig,
776}
777
778#[derive(Debug, Clone)]
780pub struct StressMemoryMetrics {
781 pub max_growth_rate: f64,
783
784 pub allocation_spikes: Vec<AllocationSpike>,
786
787 pub stress_fragmentation: f64,
789
790 pub concurrent_overhead: f64,
792
793 pub large_dataset_efficiency: f64,
795
796 pub recovery_time_seconds: f64,
798}
799
800#[derive(Debug, Clone)]
802pub struct AllocationSpike {
803 pub timestamp: Instant,
805
806 pub spike_size: usize,
808
809 pub duration: Duration,
811
812 pub stresscondition: String,
814}
815
816#[derive(Debug, Clone)]
818pub struct MemorySnapshot {
819 pub timestamp: Instant,
821
822 pub total_memory: usize,
824
825 pub category_breakdown: HashMap<String, usize>,
827
828 pub system_pressure: f64,
830
831 pub active_stressconditions: Vec<String>,
833}
834
835#[derive(Debug, Clone)]
837pub struct MemoryPressureIndicators {
838 pub system_memory_utilization: f64,
840
841 pub available_memory: usize,
843
844 pub allocation_failure_rate: f64,
846
847 pub gc_frequency: f64,
849
850 pub swap_utilization: f64,
852}
853
854impl Default for MemoryPressureIndicators {
855 fn default() -> Self {
856 #[cfg(target_pointer_width = "32")]
857 let available_memory = 512 * 1024 * 1024; #[cfg(target_pointer_width = "64")]
859 let available_memory = 8usize * 1024 * 1024 * 1024; Self {
862 system_memory_utilization: 0.0,
863 available_memory,
864 allocation_failure_rate: 0.0,
865 gc_frequency: 0.0,
866 swap_utilization: 0.0,
867 }
868 }
869}
870
871#[derive(Debug, Clone)]
873pub struct StressProfilingConfig {
874 pub snapshot_interval: Duration,
876
877 pub max_snapshots: usize,
879
880 pub spike_threshold: usize,
882
883 pub monitor_system_pressure: bool,
885
886 pub detailed_category_tracking: bool,
888}
889
890impl Default for StressProfilingConfig {
891 fn default() -> Self {
892 Self {
893 snapshot_interval: Duration::from_millis(100), max_snapshots: 10000, spike_threshold: 10 * 1024 * 1024, monitor_system_pressure: true,
897 detailed_category_tracking: true,
898 }
899 }
900}
901
902impl StressMemoryProfiler {
903 pub fn new(name: impl Into<String>, config: Option<StressProfilingConfig>) -> Self {
905 Self {
906 base_monitor: MemoryMonitor::new(name),
907 stress_metrics: StressMemoryMetrics {
908 max_growth_rate: 0.0,
909 allocation_spikes: Vec::new(),
910 stress_fragmentation: 0.0,
911 concurrent_overhead: 0.0,
912 large_dataset_efficiency: 1.0,
913 recovery_time_seconds: 0.0,
914 },
915 stress_history: VecDeque::new(),
916 pressure_indicators: MemoryPressureIndicators::default(),
917 stress_config: config.unwrap_or_default(),
918 }
919 }
920
921 pub fn start_stress_profiling(&mut self, stresscondition: &str) {
923 println!("Starting stress memory profiling for: {}", stresscondition);
924
925 self.take_memory_snapshot(vec![stresscondition.to_string()]);
927
928 self.update_system_pressure();
930 }
931
932 pub fn track_stress_allocation(
934 &mut self,
935 sizebytes: usize,
936 category: impl Into<String>,
937 stresscondition: &str,
938 ) {
939 let category = category.into();
940
941 self.base_monitor.track_allocation(sizebytes, &category);
943
944 if sizebytes >= self.stress_config.spike_threshold {
946 self.stress_metrics.allocation_spikes.push(AllocationSpike {
947 timestamp: Instant::now(),
948 spike_size: sizebytes,
949 duration: Duration::from_millis(0), stresscondition: stresscondition.to_string(),
951 });
952 }
953
954 if self.should_take_snapshot() {
956 self.take_memory_snapshot(vec![stresscondition.to_string()]);
957 }
958
959 self.update_growth_rate();
961 }
962
963 pub fn track_stress_deallocation(&mut self, sizebytes: usize, category: impl Into<String>) {
965 self.base_monitor.track_deallocation(sizebytes, category);
966
967 self.update_growth_rate();
969 }
970
971 fn take_memory_snapshot(&mut self, active_stressconditions: Vec<String>) {
973 let snapshot = MemorySnapshot {
974 timestamp: Instant::now(),
975 total_memory: self.base_monitor.current_memory_bytes,
976 category_breakdown: self.base_monitor.allocations.clone(),
977 system_pressure: self.calculate_system_pressure(),
978 active_stressconditions,
979 };
980
981 self.stress_history.push_back(snapshot);
982
983 if self.stress_history.len() > self.stress_config.max_snapshots {
985 self.stress_history.pop_front();
986 }
987 }
988
989 fn should_take_snapshot(&self) -> bool {
991 if let Some(last_snapshot) = self.stress_history.back() {
992 last_snapshot.timestamp.elapsed() >= self.stress_config.snapshot_interval
993 } else {
994 true }
996 }
997
998 fn update_growth_rate(&mut self) {
1000 if self.stress_history.len() >= 2 {
1001 let recent_snapshots: Vec<_> = self.stress_history.iter().rev().take(10).collect();
1002
1003 if recent_snapshots.len() >= 2 {
1004 let latest = recent_snapshots[0];
1005 let previous = recent_snapshots[recent_snapshots.len() - 1];
1006
1007 let memory_delta = latest.total_memory as i64 - previous.total_memory as i64;
1008 let time_delta = latest
1009 .timestamp
1010 .duration_since(previous.timestamp)
1011 .as_secs_f64();
1012
1013 if time_delta > 0.0 {
1014 let growth_rate = memory_delta as f64 / time_delta;
1015 self.stress_metrics.max_growth_rate =
1016 self.stress_metrics.max_growth_rate.max(growth_rate);
1017 }
1018 }
1019 }
1020 }
1021
1022 fn update_system_pressure(&mut self) {
1024 #[cfg(target_pointer_width = "32")]
1028 let total_system_memory: u64 = 1024 * 1024 * 1024; #[cfg(target_pointer_width = "64")]
1030 let total_system_memory: u64 = 16u64 * 1024 * 1024 * 1024; let our_usage = self.base_monitor.current_memory_bytes;
1033
1034 self.pressure_indicators.system_memory_utilization =
1035 (our_usage as f64 / total_system_memory as f64 * 100.0).min(100.0);
1036
1037 self.pressure_indicators.available_memory =
1038 (total_system_memory as usize).saturating_sub(our_usage);
1039
1040 self.pressure_indicators.allocation_failure_rate =
1042 if self.pressure_indicators.system_memory_utilization > 90.0 {
1043 0.1
1044 } else {
1045 0.0
1046 };
1047 }
1048
1049 fn calculate_system_pressure(&self) -> f64 {
1051 let pressure_factors = [
1052 self.pressure_indicators.system_memory_utilization / 100.0,
1053 self.pressure_indicators.allocation_failure_rate,
1054 self.pressure_indicators.swap_utilization / 100.0,
1055 ];
1056
1057 pressure_factors.iter().sum::<f64>() / pressure_factors.len() as f64
1058 }
1059
1060 pub fn analyze_large_dataset_efficiency(
1062 &mut self,
1063 dataset_size: usize,
1064 expected_memory: usize,
1065 ) {
1066 let actual_memory = self.base_monitor.current_memory_bytes;
1067
1068 self.stress_metrics.large_dataset_efficiency =
1069 expected_memory as f64 / actual_memory.max(1) as f64;
1070
1071 println!(
1072 "Large dataset efficiency for {} elements: {:.2} (expected: {}MB, actual: {}MB)",
1073 dataset_size,
1074 self.stress_metrics.large_dataset_efficiency,
1075 expected_memory / (1024 * 1024),
1076 actual_memory / (1024 * 1024)
1077 );
1078 }
1079
1080 pub fn analyze_concurrent_overhead(
1082 &mut self,
1083 baseline_memory: usize,
1084 concurrent_threads: usize,
1085 ) {
1086 let current_memory = self.base_monitor.current_memory_bytes;
1087 let overhead = current_memory.saturating_sub(baseline_memory);
1088
1089 self.stress_metrics.concurrent_overhead = overhead as f64 / concurrent_threads as f64;
1090
1091 println!(
1092 "Concurrent access overhead: {:.1}KB per thread ({} threads)",
1093 self.stress_metrics.concurrent_overhead / 1024.0,
1094 concurrent_threads
1095 );
1096 }
1097
1098 pub fn measure_recovery_time(&mut self, stress_endtime: Instant) {
1100 let _recovery_start_memory = self.base_monitor.current_memory_bytes;
1101
1102 let recovery_time = Instant::now().duration_since(stress_endtime);
1104 self.stress_metrics.recovery_time_seconds = recovery_time.as_secs_f64();
1105
1106 println!(
1107 "Memory recovery _time: {:.2}s",
1108 self.stress_metrics.recovery_time_seconds
1109 );
1110 }
1111
1112 pub fn generate_stress_report(&self) -> StressMemoryReport {
1114 let base_report = self.base_monitor.generate_report();
1115
1116 let memory_pressure_analysis = self.analyze_memory_pressure();
1117 let allocation_pattern_analysis = self.analyze_allocation_patterns();
1118 let stress_performance_analysis = self.analyze_stress_performance();
1119
1120 StressMemoryReport {
1121 base_report,
1122 stress_metrics: self.stress_metrics.clone(),
1123 memory_pressure_analysis,
1124 allocation_pattern_analysis,
1125 stress_performance_analysis,
1126 system_pressure: self.pressure_indicators.clone(),
1127 snapshot_count: self.stress_history.len(),
1128 stress_recommendations: self.generate_stress_recommendations(),
1129 }
1130 }
1131
1132 fn analyze_memory_pressure(&self) -> MemoryPressureAnalysis {
1134 let max_pressure = self
1135 .stress_history
1136 .iter()
1137 .map(|s| s.system_pressure)
1138 .fold(0.0, f64::max);
1139
1140 let avg_pressure = if !self.stress_history.is_empty() {
1141 self.stress_history
1142 .iter()
1143 .map(|s| s.system_pressure)
1144 .sum::<f64>()
1145 / self.stress_history.len() as f64
1146 } else {
1147 0.0
1148 };
1149
1150 let pressure_spikes = self
1151 .stress_history
1152 .iter()
1153 .filter(|s| s.system_pressure > 0.8)
1154 .count();
1155
1156 MemoryPressureAnalysis {
1157 max_pressure,
1158 avg_pressure,
1159 pressure_spikes,
1160 critical_periods: pressure_spikes, }
1162 }
1163
1164 fn analyze_allocation_patterns(&self) -> AllocationPatternAnalysis {
1166 let spike_count = self.stress_metrics.allocation_spikes.len();
1167 let total_spike_memory: usize = self
1168 .stress_metrics
1169 .allocation_spikes
1170 .iter()
1171 .map(|s| s.spike_size)
1172 .sum();
1173
1174 let pattern_regularity = if spike_count > 1 {
1175 let intervals: Vec<_> = self
1177 .stress_metrics
1178 .allocation_spikes
1179 .windows(2)
1180 .map(|pair| {
1181 pair[1]
1182 .timestamp
1183 .duration_since(pair[0].timestamp)
1184 .as_secs_f64()
1185 })
1186 .collect();
1187
1188 if !intervals.is_empty() {
1189 let mean_interval = intervals.iter().sum::<f64>() / intervals.len() as f64;
1190 let variance = intervals
1191 .iter()
1192 .map(|&x| (x - mean_interval).powi(2))
1193 .sum::<f64>()
1194 / intervals.len() as f64;
1195 1.0 / (1.0 + variance) } else {
1197 1.0
1198 }
1199 } else {
1200 1.0
1201 };
1202
1203 AllocationPatternAnalysis {
1204 spike_count,
1205 total_spike_memory,
1206 pattern_regularity,
1207 fragmentation_level: self.stress_metrics.stress_fragmentation,
1208 }
1209 }
1210
1211 fn analyze_stress_performance(&self) -> StressPerformanceAnalysis {
1213 StressPerformanceAnalysis {
1214 max_growth_rate: self.stress_metrics.max_growth_rate,
1215 concurrent_overhead: self.stress_metrics.concurrent_overhead,
1216 large_dataset_efficiency: self.stress_metrics.large_dataset_efficiency,
1217 recovery_time: self.stress_metrics.recovery_time_seconds,
1218 overall_stress_grade: self.calculate_stress_grade(),
1219 }
1220 }
1221
1222 fn calculate_stress_grade(&self) -> StressPerformanceGrade {
1224 let factors = [
1225 if self.stress_metrics.max_growth_rate < 1024.0 * 1024.0 {
1226 1.0
1227 } else {
1228 0.0
1229 }, if self.stress_metrics.concurrent_overhead < 1024.0 * 1024.0 {
1231 1.0
1232 } else {
1233 0.0
1234 }, self.stress_metrics.large_dataset_efficiency.min(1.0), if self.stress_metrics.recovery_time_seconds < 10.0 {
1237 1.0
1238 } else {
1239 0.0
1240 }, ];
1242
1243 let score = factors.iter().sum::<f64>() / factors.len() as f64;
1244
1245 match score {
1246 s if s >= 0.9 => StressPerformanceGrade::Excellent,
1247 s if s >= 0.7 => StressPerformanceGrade::Good,
1248 s if s >= 0.5 => StressPerformanceGrade::Fair,
1249 s if s >= 0.3 => StressPerformanceGrade::Poor,
1250 _ => StressPerformanceGrade::Critical,
1251 }
1252 }
1253
1254 fn generate_stress_recommendations(&self) -> Vec<String> {
1256 let mut recommendations = Vec::new();
1257
1258 if self.stress_metrics.max_growth_rate > 10.0 * 1024.0 * 1024.0 {
1259 recommendations
1261 .push("High memory growth rate detected - consider batch processing".to_string());
1262 }
1263
1264 if self.stress_metrics.allocation_spikes.len() > 10 {
1265 recommendations
1266 .push("Frequent allocation spikes - implement memory pre-allocation".to_string());
1267 }
1268
1269 if self.stress_metrics.concurrent_overhead > 5.0 * 1024.0 * 1024.0 {
1270 recommendations
1272 .push("High concurrent overhead - review thread-local memory usage".to_string());
1273 }
1274
1275 if self.stress_metrics.large_dataset_efficiency < 0.7 {
1276 recommendations
1277 .push("Poor large dataset efficiency - optimize memory layout".to_string());
1278 }
1279
1280 if self.stress_metrics.recovery_time_seconds > 30.0 {
1281 recommendations.push("Slow memory recovery - implement explicit cleanup".to_string());
1282 }
1283
1284 recommendations
1285 }
1286}
1287
1288#[derive(Debug, Clone)]
1290pub struct StressMemoryReport {
1291 pub base_report: MemoryReport,
1293
1294 pub stress_metrics: StressMemoryMetrics,
1296
1297 pub memory_pressure_analysis: MemoryPressureAnalysis,
1299
1300 pub allocation_pattern_analysis: AllocationPatternAnalysis,
1302
1303 pub stress_performance_analysis: StressPerformanceAnalysis,
1305
1306 pub system_pressure: MemoryPressureIndicators,
1308
1309 pub snapshot_count: usize,
1311
1312 pub stress_recommendations: Vec<String>,
1314}
1315
1316#[derive(Debug, Clone)]
1318pub struct MemoryPressureAnalysis {
1319 pub max_pressure: f64,
1321
1322 pub avg_pressure: f64,
1324
1325 pub pressure_spikes: usize,
1327
1328 pub critical_periods: usize,
1330}
1331
1332#[derive(Debug, Clone)]
1334pub struct AllocationPatternAnalysis {
1335 pub spike_count: usize,
1337
1338 pub total_spike_memory: usize,
1340
1341 pub pattern_regularity: f64,
1343
1344 pub fragmentation_level: f64,
1346}
1347
1348#[derive(Debug, Clone)]
1350pub struct StressPerformanceAnalysis {
1351 pub max_growth_rate: f64,
1353
1354 pub concurrent_overhead: f64,
1356
1357 pub large_dataset_efficiency: f64,
1359
1360 pub recovery_time: f64,
1362
1363 pub overall_stress_grade: StressPerformanceGrade,
1365}
1366
1367#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1369pub enum StressPerformanceGrade {
1370 Excellent,
1371 Good,
1372 Fair,
1373 Poor,
1374 Critical,
1375}
1376
1377#[allow(dead_code)]
1379pub fn create_stress_profiler(name: impl Into<String>) -> StressMemoryProfiler {
1380 StressMemoryProfiler::new(name, None)
1381}
1382
1383#[allow(dead_code)]
1385pub fn create_stress_profiler_with_config(
1386 name: impl Into<String>,
1387 config: StressProfilingConfig,
1388) -> StressMemoryProfiler {
1389 StressMemoryProfiler::new(name, Some(config))
1390}
1391
1392#[cfg(test)]
1393mod tests {
1394 use super::*;
1395
1396 #[test]
1397 fn test_memory_monitor_basic() {
1398 let mut monitor = MemoryMonitor::new("test");
1399
1400 monitor.track_allocation(1024, "matrix");
1402 monitor.track_allocation(512, "cache");
1403
1404 assert_eq!(monitor.current_memory_bytes, 1536);
1405 assert_eq!(monitor.peak_memory_bytes, 1536);
1406
1407 monitor.track_deallocation(512, "cache");
1409 assert_eq!(monitor.current_memory_bytes, 1024);
1410
1411 monitor.track_deallocation(1024, "matrix");
1412 assert_eq!(monitor.current_memory_bytes, 0);
1413
1414 let report = monitor.generate_report();
1415 assert!(!report.has_potential_leaks());
1417 }
1418
1419 #[test]
1420 fn test_leak_detection() {
1421 let mut monitor = MemoryMonitor::new("leak_test");
1422
1423 monitor.track_allocation(2048, "leaked_memory");
1425
1426 let report = monitor.generate_report();
1427 assert!(report.leak_indicators.unmatched_allocations > 0);
1428 }
1429
1430 #[test]
1431 fn test_global_monitoring() {
1432 start_monitoring();
1433
1434 let _monitor1 = MemoryMonitor::new("global_test_1");
1435 let _monitor2 = MemoryMonitor::new("global_test_2");
1436
1437 let stats = get_global_stats().expect("Operation failed");
1438 assert!(
1440 stats.active_interpolators >= 2,
1441 "Expected at least 2 active interpolators, got {}",
1442 stats.active_interpolators
1443 );
1444
1445 stop_monitoring();
1446 }
1447
1448 #[test]
1449 fn test_stress_profiler_basic() {
1450 let mut profiler = create_stress_profiler("stress_test");
1451
1452 profiler.start_stress_profiling("large_dataset");
1453 profiler.track_stress_allocation(10 * 1024 * 1024, "large_matrix", "large_dataset");
1454 profiler.track_stress_allocation(5 * 1024 * 1024, "cache", "large_dataset");
1455
1456 let report = profiler.generate_stress_report();
1457 assert!(report.stress_metrics.allocation_spikes.len() > 0);
1458 assert!(report.snapshot_count > 0);
1459 }
1460
1461 #[test]
1462 fn test_stress_allocation_spike_detection() {
1463 let mut profiler = create_stress_profiler("spike_test");
1464
1465 profiler.track_stress_allocation(15 * 1024 * 1024, "spike", "stress_test");
1467
1468 assert_eq!(profiler.stress_metrics.allocation_spikes.len(), 1);
1469 assert_eq!(
1470 profiler.stress_metrics.allocation_spikes[0].spike_size,
1471 15 * 1024 * 1024
1472 );
1473 }
1474}