1use std::collections::{HashMap, VecDeque};
7use std::time::{Duration, Instant};
8
9use crate::memory::metrics::{MemoryEvent, MemoryEventType};
10
11#[cfg(feature = "memory_metrics")]
12#[cfg(feature = "serialization")]
13use serde::{Deserialize, Serialize};
14
15#[derive(Debug, Clone)]
17pub struct LeakDetectionConfig {
18 pub analysis_windowsize: usize,
20 pub leak_threshold_bytes: usize,
22 pub leak_threshold_duration: Duration,
24 pub growth_rate_threshold: f64,
26 pub min_data_points: usize,
28}
29
30impl Default for LeakDetectionConfig {
31 fn default() -> Self {
32 Self {
33 analysis_windowsize: 100,
34 leak_threshold_bytes: 1024 * 1024, leak_threshold_duration: Duration::from_secs(30),
36 growth_rate_threshold: 1024.0, min_data_points: 10,
38 }
39 }
40}
41
42#[derive(Debug, Clone)]
44#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
45pub struct MemoryPatternAnalysis {
46 pub component: String,
48 pub patterns: Vec<AllocationPattern>,
50 pub efficiency: MemoryEfficiencyMetrics,
52 pub potential_issues: Vec<MemoryIssue>,
54 pub recommendations: Vec<OptimizationRecommendation>,
56}
57
58#[derive(Debug, Clone)]
60#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
61pub enum AllocationPattern {
62 SteadyGrowth {
64 rate: f64,
66 confidence: f64,
68 },
69 PeriodicCycle {
71 cycle_duration: Duration,
73 peak_size: usize,
75 confidence: f64,
77 },
78 BurstAllocation {
80 burst_size: usize,
82 burst_duration: Duration,
84 confidence: f64,
86 },
87 Plateau {
89 size: usize,
91 duration: Duration,
93 confidence: f64,
95 },
96}
97
98#[derive(Debug, Clone)]
100#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
101pub struct MemoryEfficiencyMetrics {
102 pub reuse_ratio: f64,
104 pub allocation_frequency: f64,
106 pub avg_allocation_lifetime: Duration,
108 pub fragmentation_estimate: f64,
110 pub buffer_pool_efficiency: Option<f64>,
112}
113
114#[derive(Debug, Clone)]
116#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
117pub enum MemoryIssue {
118 MemoryLeak {
120 growth_rate: f64,
122 duration: Duration,
124 severity: f64,
126 },
127 HighAllocationFrequency {
129 frequency: f64,
131 impact: String,
133 },
134 HighPeakUsage {
136 peak_size: usize,
138 system_percentage: Option<f64>,
140 },
141 MemoryFragmentation {
143 fragmentation_ratio: f64,
145 potential_waste: usize,
147 },
148 IneffientBufferPool {
150 efficiency: f64,
152 pool_misses: usize,
154 },
155}
156
157#[derive(Debug, Clone)]
159#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
160pub enum OptimizationRecommendation {
161 UseBufferPooling {
163 expected_savings: usize,
165 suggested_poolsizes: Vec<usize>,
167 },
168 BatchAllocations {
170 current_frequency: f64,
172 suggested_batch_size: usize,
174 },
175 PreAllocateMemory {
177 suggested_size: usize,
179 performance_gain: String,
181 },
182 UseMemoryEfficientStructures {
184 current_type: String,
186 suggested_alternative: String,
188 memory_reduction: usize,
190 },
191 ImplementCompaction {
193 fragmentation_reduction: f64,
195 suggested_frequency: Duration,
197 },
198}
199
200#[derive(Debug, Clone)]
202#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
203pub struct LeakDetectionResult {
204 pub component: String,
206 pub leak_detected: bool,
208 pub growth_rate: f64,
210 pub confidence: f64,
212 pub analysis_duration: Duration,
214 pub current_usage: usize,
216 pub projected_usage_1h: usize,
218 pub projected_usage_24h: usize,
220}
221
222pub struct MemoryAnalytics {
224 leak_config: LeakDetectionConfig,
226 usage_history: HashMap<String, VecDeque<(Instant, usize)>>,
228 allocation_history: HashMap<String, VecDeque<(Instant, usize, MemoryEventType)>>,
230}
231
232impl MemoryAnalytics {
233 pub fn new(leakconfig: LeakDetectionConfig) -> Self {
235 Self {
236 leak_config: leakconfig,
237 usage_history: HashMap::new(),
238 allocation_history: HashMap::new(),
239 }
240 }
241
242 pub fn record_event(&mut self, event: MemoryEvent) {
244 let component = event.component.clone();
245 let timestamp = event.timestamp;
246
247 let alloc_history = self
249 .allocation_history
250 .entry(component.clone())
251 .or_default();
252 alloc_history.push_back((timestamp, event.size, event.event_type));
253
254 while alloc_history.len() > self.leak_config.analysis_windowsize {
256 alloc_history.pop_front();
257 }
258
259 let current_usage = self.calculate_current_usage(&component);
261
262 let usage_history = self.usage_history.entry(component).or_default();
264 usage_history.push_back((timestamp, current_usage));
265
266 while usage_history.len() > self.leak_config.analysis_windowsize {
268 usage_history.pop_front();
269 }
270 }
271
272 fn calculate_current_usage(&self, component: &str) -> usize {
274 if let Some(history) = self.allocation_history.get(component) {
275 let mut usage = 0usize;
276 for (_timestamp, size, event_type) in history {
277 match event_type {
278 MemoryEventType::Allocation => usage += size,
279 MemoryEventType::Deallocation => usage = usage.saturating_sub(*size),
280 MemoryEventType::Resize => {
281 usage += size;
284 }
285 MemoryEventType::Access | MemoryEventType::Transfer => {
286 }
288 }
289 }
290 usage
291 } else {
292 0
293 }
294 }
295
296 pub fn detect_memory_leak(&self, component: &str) -> Option<LeakDetectionResult> {
298 let usage_history = self.usage_history.get(component)?;
299
300 if usage_history.len() < self.leak_config.min_data_points {
301 return None;
302 }
303
304 let (growth_rate, confidence) = self.calculate_growth_rate(usage_history);
306
307 let analysis_duration = usage_history
308 .back()?
309 .0
310 .duration_since(usage_history.front()?.0);
311
312 if analysis_duration < self.leak_config.leak_threshold_duration {
313 return None;
314 }
315
316 let current_usage = usage_history.back()?.1;
317 let leak_detected = growth_rate > self.leak_config.growth_rate_threshold
318 && confidence > 0.7
319 && current_usage > self.leak_config.leak_threshold_bytes;
320
321 Some(LeakDetectionResult {
322 component: component.to_string(),
323 leak_detected,
324 growth_rate,
325 confidence,
326 analysis_duration,
327 current_usage,
328 projected_usage_1h: current_usage + (growth_rate * 3600.0) as usize,
329 projected_usage_24h: current_usage + (growth_rate * 86400.0) as usize,
330 })
331 }
332
333 fn calculate_growth_rate(&self, history: &VecDeque<(Instant, usize)>) -> (f64, f64) {
335 if history.len() < 2 {
336 return (0.0, 0.0);
337 }
338
339 let start_time = history.front().expect("Operation failed").0;
340 let points: Vec<(f64, f64)> = history
341 .iter()
342 .map(|(timestamp, usage)| {
343 let x = timestamp.duration_since(start_time).as_secs_f64();
344 let y = *usage as f64;
345 (x, y)
346 })
347 .collect();
348
349 self.linear_regression(&points)
350 }
351
352 fn linear_regression(&self, points: &[(f64, f64)]) -> (f64, f64) {
354 let n = points.len() as f64;
355 if n < 2.0 {
356 return (0.0, 0.0);
357 }
358
359 let sum_x: f64 = points.iter().map(|(x, _)| x).sum();
360 let sum_y: f64 = points.iter().map(|(_, y)| y).sum();
361 let sum_xy: f64 = points.iter().map(|(x, y)| x * y).sum();
362 let sum_x2: f64 = points.iter().map(|(x, _)| x * x).sum();
363 let sum_y2: f64 = points.iter().map(|(_, y)| y * y).sum();
364
365 let mean_x = sum_x / n;
366 let mean_y = sum_y / n;
367
368 let numerator = sum_xy - n * mean_x * mean_y;
369 let denominator = sum_x2 - n * mean_x * mean_x;
370
371 if denominator.abs() < f64::EPSILON {
372 return (0.0, 0.0);
373 }
374
375 let slope = numerator / denominator;
376
377 let ss_tot = sum_y2 - n * mean_y * mean_y;
379 let ss_res: f64 = points
380 .iter()
381 .map(|(x, y)| {
382 let predicted = slope * (x - mean_x) + mean_y;
383 (y - predicted).powi(2)
384 })
385 .sum();
386
387 let r_squared = if ss_tot.abs() < f64::EPSILON {
388 0.0
389 } else {
390 1.0 - (ss_res / ss_tot).max(0.0)
391 };
392
393 (slope, r_squared)
394 }
395
396 pub fn analyze_patterns(&self, component: &str) -> Option<MemoryPatternAnalysis> {
398 let usage_history = self.usage_history.get(component)?;
399 let allocation_history = self.allocation_history.get(component)?;
400
401 if usage_history.len() < self.leak_config.min_data_points {
402 return None;
403 }
404
405 let patterns = self.detect_allocation_patterns(usage_history, allocation_history);
406 let efficiency = self.calculate_efficiency_metrics(component, allocation_history);
407 let potential_issues = self.identify_potential_issues(component);
408 let recommendations = self.generate_recommendations(&efficiency, &potential_issues);
409
410 Some(MemoryPatternAnalysis {
411 component: component.to_string(),
412 patterns,
413 efficiency,
414 potential_issues,
415 recommendations,
416 })
417 }
418
419 fn detect_allocation_patterns(
421 &self,
422 usage_history: &VecDeque<(Instant, usize)>,
423 allocation_history: &VecDeque<(Instant, usize, MemoryEventType)>,
424 ) -> Vec<AllocationPattern> {
425 let mut patterns = Vec::new();
426
427 let (growth_rate, confidence) = self.calculate_growth_rate(usage_history);
429 if growth_rate > 100.0 && confidence > 0.8 {
430 patterns.push(AllocationPattern::SteadyGrowth {
431 rate: growth_rate,
432 confidence,
433 });
434 }
435
436 if let Some(cycle) = self.detect_periodic_cycles(usage_history) {
438 patterns.push(cycle);
439 }
440
441 if let Some(burst) = self.detect_burst_allocations(allocation_history) {
443 patterns.push(burst);
444 }
445
446 if let Some(plateau) = self.detect_plateaus(usage_history) {
448 patterns.push(plateau);
449 }
450
451 patterns
452 }
453
454 fn detect_periodic_cycles(
456 &self,
457 usage_history: &VecDeque<(Instant, usize)>,
458 ) -> Option<AllocationPattern> {
459 if usage_history.len() < 10 {
463 return None;
464 }
465
466 let values: Vec<usize> = usage_history.iter().map(|(_, usage)| *usage).collect();
468
469 for cycle_len in 3..values.len() / 3 {
471 let mut correlation = 0.0;
472 let mut count = 0;
473
474 for i in cycle_len..values.len() {
475 let diff = (values[i] as f64 - values[i - cycle_len] as f64).abs();
476 let avg = (values[i] + values[i - cycle_len]) as f64 / 2.0;
477 if avg > 0.0 {
478 correlation += 1.0 - (diff / avg).min(1.0);
479 count += 1;
480 }
481 }
482
483 if count > 0 {
484 correlation /= count as f64;
485 if correlation > 0.8 {
486 let cycle_duration = Duration::from_secs((cycle_len * 5) as u64); let peak_size = values.iter().max().copied().unwrap_or(0);
488
489 return Some(AllocationPattern::PeriodicCycle {
490 cycle_duration,
491 peak_size,
492 confidence: correlation,
493 });
494 }
495 }
496 }
497
498 None
499 }
500
501 fn detect_burst_allocations(
503 &self,
504 allocation_history: &VecDeque<(Instant, usize, MemoryEventType)>,
505 ) -> Option<AllocationPattern> {
506 if allocation_history.len() < 5 {
507 return None;
508 }
509
510 let mut burst_size = 0usize;
512 let mut burst_start: Option<Instant> = None;
513 let mut current_burst_size = 0usize;
514
515 for (timestamp, size, event_type) in allocation_history {
516 match event_type {
517 MemoryEventType::Allocation => {
518 if burst_start.is_none() {
519 burst_start = Some(*timestamp);
520 current_burst_size = *size;
521 } else {
522 current_burst_size += size;
523 }
524 }
525 MemoryEventType::Deallocation => {
526 if let Some(start) = burst_start {
527 let duration = timestamp.duration_since(start);
528 if duration > Duration::from_millis(100) && current_burst_size > burst_size
529 {
530 burst_size = current_burst_size;
531 }
532 burst_start = None;
533 current_burst_size = 0;
534 }
535 }
536 _ => {}
537 }
538 }
539
540 if burst_size > 1024 * 1024 {
541 Some(AllocationPattern::BurstAllocation {
543 burst_size,
544 burst_duration: Duration::from_millis(500), confidence: 0.9,
546 })
547 } else {
548 None
549 }
550 }
551
552 fn detect_plateaus(
554 &self,
555 usage_history: &VecDeque<(Instant, usize)>,
556 ) -> Option<AllocationPattern> {
557 if usage_history.len() < 10 {
558 return None;
559 }
560
561 let values: Vec<usize> = usage_history.iter().map(|(_, usage)| *usage).collect();
562
563 let mut plateau_start = 0;
565 let mut max_plateau_len = 0;
566 let mut plateau_value = 0;
567
568 for i in 1..values.len() {
569 let diff_ratio = if values[i.saturating_sub(1)] > 0 {
570 (values[i] as f64 - values[i.saturating_sub(1)] as f64).abs()
571 / values[i.saturating_sub(1)] as f64
572 } else {
573 0.0
574 };
575
576 if diff_ratio < 0.05 {
577 if plateau_start == 0 {
579 plateau_start = i.saturating_sub(1);
580 }
581 } else {
582 if plateau_start > 0 {
583 let plateau_len = i - plateau_start;
584 if plateau_len > max_plateau_len {
585 max_plateau_len = plateau_len;
586 plateau_value = values[plateau_start];
587 }
588 }
589 plateau_start = 0;
590 }
591 }
592
593 if plateau_start > 0 {
595 let plateau_len = values.len() - plateau_start;
596 if plateau_len > max_plateau_len {
597 max_plateau_len = plateau_len;
598 plateau_value = values[plateau_start];
599 }
600 }
601
602 if max_plateau_len >= 5 && plateau_value > 0 {
603 Some(AllocationPattern::Plateau {
604 size: plateau_value,
605 duration: Duration::from_secs((max_plateau_len * 5) as u64), confidence: 0.8,
607 })
608 } else {
609 None
610 }
611 }
612
613 fn calculate_efficiency_metrics(
615 &self,
616 component: &str,
617 allocation_history: &VecDeque<(Instant, usize, MemoryEventType)>,
618 ) -> MemoryEfficiencyMetrics {
619 if allocation_history.is_empty() {
620 return MemoryEfficiencyMetrics {
621 reuse_ratio: 0.0,
622 allocation_frequency: 0.0,
623 avg_allocation_lifetime: Duration::from_secs(0),
624 fragmentation_estimate: 0.0,
625 buffer_pool_efficiency: None,
626 };
627 }
628
629 let total_allocated = allocation_history
631 .iter()
632 .filter_map(|(_, size, event_type)| {
633 if matches!(event_type, MemoryEventType::Allocation) {
634 Some(*size)
635 } else {
636 None
637 }
638 })
639 .sum::<usize>();
640
641 let current_usage = self.calculate_current_usage(component);
642 let reuse_ratio = if current_usage > 0 {
643 total_allocated as f64 / current_usage as f64
644 } else {
645 0.0
646 };
647
648 let allocation_count = allocation_history
649 .iter()
650 .filter(|(_, _, event_type)| matches!(event_type, MemoryEventType::Allocation))
651 .count();
652
653 let duration = if let (Some(first), Some(last)) =
654 (allocation_history.front(), allocation_history.back())
655 {
656 last.0.duration_since(first.0)
657 } else {
658 Duration::from_secs(1)
659 };
660
661 let allocation_frequency = allocation_count as f64 / duration.as_secs_f64();
662
663 let avg_allocation_lifetime = if allocation_count > 0 {
665 duration / allocation_count as u32
666 } else {
667 Duration::from_secs(0)
668 };
669
670 let fragmentation_estimate = self.estimate_fragmentation(allocation_history);
672
673 MemoryEfficiencyMetrics {
674 reuse_ratio,
675 allocation_frequency,
676 avg_allocation_lifetime,
677 fragmentation_estimate,
678 buffer_pool_efficiency: None, }
680 }
681
682 fn estimate_fragmentation(
684 &self,
685 allocation_history: &VecDeque<(Instant, usize, MemoryEventType)>,
686 ) -> f64 {
687 let allocation_sizes: Vec<usize> = allocation_history
691 .iter()
692 .filter_map(|(_, size, event_type)| {
693 if matches!(event_type, MemoryEventType::Allocation) {
694 Some(*size)
695 } else {
696 None
697 }
698 })
699 .collect();
700
701 if allocation_sizes.is_empty() {
702 return 0.0;
703 }
704
705 let mean = allocation_sizes.iter().sum::<usize>() as f64 / allocation_sizes.len() as f64;
707 let variance = allocation_sizes
708 .iter()
709 .map(|&size| {
710 let diff = size as f64 - mean;
711 diff * diff
712 })
713 .sum::<f64>()
714 / allocation_sizes.len() as f64;
715
716 let std_dev = variance.sqrt();
717
718 if mean > 0.0 {
719 (std_dev / mean).min(1.0)
720 } else {
721 0.0
722 }
723 }
724
725 fn identify_potential_issues(&self, component: &str) -> Vec<MemoryIssue> {
727 let mut issues = Vec::new();
728
729 if let Some(leak_result) = self.detect_memory_leak(component) {
731 if leak_result.leak_detected {
732 issues.push(MemoryIssue::MemoryLeak {
733 growth_rate: leak_result.growth_rate,
734 duration: leak_result.analysis_duration,
735 severity: leak_result.confidence,
736 });
737 }
738 }
739
740 if let Some(allocation_history) = self.allocation_history.get(component) {
742 let efficiency = self.calculate_efficiency_metrics(component, allocation_history);
743
744 if efficiency.allocation_frequency > 100.0 {
745 issues.push(MemoryIssue::HighAllocationFrequency {
747 frequency: efficiency.allocation_frequency,
748 impact: "High allocation frequency can cause performance degradation"
749 .to_string(),
750 });
751 }
752
753 if efficiency.fragmentation_estimate > 0.7 {
754 let current_usage = self.calculate_current_usage(component);
755 let potential_waste =
756 (current_usage as f64 * efficiency.fragmentation_estimate) as usize;
757
758 issues.push(MemoryIssue::MemoryFragmentation {
759 fragmentation_ratio: efficiency.fragmentation_estimate,
760 potential_waste,
761 });
762 }
763 }
764
765 if let Some(usage_history) = self.usage_history.get(component) {
767 if let Some(peak_usage) = usage_history.iter().map(|(_, usage)| *usage).max() {
768 if peak_usage > 100 * 1024 * 1024 {
769 issues.push(MemoryIssue::HighPeakUsage {
771 peak_size: peak_usage,
772 system_percentage: None, });
774 }
775 }
776 }
777
778 issues
779 }
780
781 fn generate_recommendations(
783 &self,
784 efficiency: &MemoryEfficiencyMetrics,
785 issues: &[MemoryIssue],
786 ) -> Vec<OptimizationRecommendation> {
787 let mut recommendations = Vec::new();
788
789 if efficiency.allocation_frequency > 50.0 {
791 recommendations.push(OptimizationRecommendation::UseBufferPooling {
792 expected_savings: (efficiency.allocation_frequency * 1024.0) as usize, suggested_poolsizes: vec![1024, 4096, 16384, 65536],
794 });
795 }
796
797 if efficiency.allocation_frequency > 20.0
799 && efficiency.avg_allocation_lifetime.as_secs() < 10
800 {
801 recommendations.push(OptimizationRecommendation::BatchAllocations {
802 current_frequency: efficiency.allocation_frequency,
803 suggested_batch_size: (efficiency.allocation_frequency * 2.0) as usize,
804 });
805 }
806
807 if efficiency.reuse_ratio > 2.0 {
809 recommendations.push(OptimizationRecommendation::PreAllocateMemory {
810 suggested_size: (efficiency.allocation_frequency * 1024.0) as usize,
811 performance_gain: "Reduced allocation overhead".to_string(),
812 });
813 }
814
815 if efficiency.fragmentation_estimate > 0.5 {
817 recommendations.push(OptimizationRecommendation::ImplementCompaction {
818 fragmentation_reduction: efficiency.fragmentation_estimate * 0.7, suggested_frequency: Duration::from_secs(300), });
821 }
822
823 for issue in issues {
825 if let MemoryIssue::HighPeakUsage { peak_size, .. } = issue {
826 recommendations.push(OptimizationRecommendation::UseMemoryEfficientStructures {
827 current_type: "Unknown".to_string(),
828 suggested_alternative: "Streaming or memory-mapped structures".to_string(),
829 memory_reduction: peak_size / 2, });
831 }
832 }
833
834 recommendations
835 }
836
837 pub fn get_leak_detection_results(&self) -> Vec<LeakDetectionResult> {
839 self.usage_history
840 .keys()
841 .filter_map(|component| self.detect_memory_leak(component))
842 .collect()
843 }
844
845 pub fn get_pattern_analysis_results(&self) -> Vec<MemoryPatternAnalysis> {
847 self.usage_history
848 .keys()
849 .filter_map(|component| self.analyze_patterns(component))
850 .collect()
851 }
852
853 pub fn clear(&mut self) {
855 self.usage_history.clear();
856 self.allocation_history.clear();
857 }
858}
859
860impl Default for MemoryAnalytics {
861 fn default() -> Self {
862 Self::new(LeakDetectionConfig::default())
863 }
864}
865
866#[cfg(test)]
867mod tests {
868 use super::*;
869
870 #[test]
871 fn test_memory_analytics_creation() {
872 let analytics = MemoryAnalytics::new(LeakDetectionConfig::default());
873 assert!(analytics.usage_history.is_empty());
874 assert!(analytics.allocation_history.is_empty());
875 }
876
877 #[test]
878 fn test_leak_detection_insufficient_data() {
879 let analytics = MemoryAnalytics::new(LeakDetectionConfig::default());
880 let result = analytics.detect_memory_leak("test_component");
881 assert!(result.is_none());
882 }
883
884 #[test]
885 fn test_linear_regression() {
886 let analytics = MemoryAnalytics::new(LeakDetectionConfig::default());
887
888 let points = vec![(0.0, 0.0), (1.0, 100.0), (2.0, 200.0), (3.0, 300.0)];
890
891 let (slope, r_squared) = analytics.linear_regression(&points);
892 assert!((slope - 100.0).abs() < 1.0);
893 assert!(r_squared > 0.99);
894 }
895
896 #[test]
897 fn test_pattern_analysis_with_insufficient_data() {
898 let analytics = MemoryAnalytics::new(LeakDetectionConfig::default());
899 let result = analytics.analyze_patterns("test_component");
900 assert!(result.is_none());
901 }
902}