sorting_race/models/
memory_metrics.rs

1//! Memory metrics model for real-time memory tracking
2
3use crate::models::session::AlgorithmType;
4use std::collections::HashMap;
5use std::time::Instant;
6
7/// Memory metrics for a single algorithm
8#[derive(Debug, Clone, PartialEq)]
9pub struct MemoryMetrics {
10    /// Algorithm this metric belongs to
11    pub algorithm_type: AlgorithmType,
12    /// Current memory usage in bytes
13    pub current_usage_bytes: usize,
14    /// Peak memory usage in bytes seen so far
15    pub peak_usage_bytes: usize,
16    /// When this metric was last updated
17    pub last_updated: Instant,
18}
19
20impl MemoryMetrics {
21    /// Create new memory metrics for an algorithm
22    pub fn new(algorithm_type: AlgorithmType) -> Self {
23        Self {
24            algorithm_type,
25            current_usage_bytes: 0,
26            peak_usage_bytes: 0,
27            last_updated: Instant::now(),
28        }
29    }
30
31    /// Update memory usage with new value
32    pub fn update(&mut self, current_bytes: usize) {
33        self.current_usage_bytes = current_bytes;
34        if current_bytes > self.peak_usage_bytes {
35            self.peak_usage_bytes = current_bytes;
36        }
37        self.last_updated = Instant::now();
38    }
39
40    /// Reset metrics to zero
41    pub fn reset(&mut self) {
42        self.current_usage_bytes = 0;
43        self.peak_usage_bytes = 0;
44        self.last_updated = Instant::now();
45    }
46
47    /// Get age of this metric (time since last update)
48    pub fn age(&self) -> std::time::Duration {
49        self.last_updated.elapsed()
50    }
51
52    /// Check if this metric is stale (hasn't been updated recently)
53    pub fn is_stale(&self, threshold: std::time::Duration) -> bool {
54        self.age() > threshold
55    }
56
57    /// Format current memory usage as human-readable string
58    pub fn format_current(&self) -> String {
59        Self::format_bytes(self.current_usage_bytes)
60    }
61
62    /// Format peak memory usage as human-readable string
63    pub fn format_peak(&self) -> String {
64        Self::format_bytes(self.peak_usage_bytes)
65    }
66
67    /// Format bytes into human-readable string
68    pub fn format_bytes(bytes: usize) -> String {
69        const UNITS: &[&str] = &["B", "KB", "MB", "GB"];
70        let mut size = bytes as f64;
71        let mut unit_index = 0;
72
73        while size >= 1024.0 && unit_index < UNITS.len() - 1 {
74            size /= 1024.0;
75            unit_index += 1;
76        }
77
78        if unit_index == 0 {
79            format!("{}B", bytes)
80        } else {
81            format!("{:.1}{}", size, UNITS[unit_index])
82        }
83    }
84
85    /// Get memory efficiency ratio (current / peak)
86    pub fn efficiency_ratio(&self) -> f64 {
87        if self.peak_usage_bytes == 0 {
88            1.0
89        } else {
90            self.current_usage_bytes as f64 / self.peak_usage_bytes as f64
91        }
92    }
93
94    /// Check if memory usage is currently at peak
95    pub fn is_at_peak(&self) -> bool {
96        self.current_usage_bytes == self.peak_usage_bytes
97    }
98}
99
100/// Container for tracking memory metrics across multiple algorithms
101#[derive(Debug, Clone)]
102pub struct MemoryMetricsCollection {
103    /// Metrics for each algorithm
104    metrics: HashMap<AlgorithmType, MemoryMetrics>,
105    /// Global peak memory across all algorithms
106    global_peak: usize,
107    /// When the collection was created
108    start_time: Instant,
109    /// Whether real-time updates are enabled
110    real_time_enabled: bool,
111    /// Update frequency for real-time updates
112    update_frequency: std::time::Duration,
113}
114
115impl MemoryMetricsCollection {
116    /// Create new memory metrics collection
117    pub fn new() -> Self {
118        Self {
119            metrics: HashMap::new(),
120            global_peak: 0,
121            start_time: Instant::now(),
122            real_time_enabled: true,
123            update_frequency: std::time::Duration::from_millis(100), // 10 FPS updates
124        }
125    }
126
127    /// Create collection with specific algorithms
128    pub fn with_algorithms(algorithms: &[AlgorithmType]) -> Self {
129        let mut collection = Self::new();
130        for &algorithm in algorithms {
131            collection.add_algorithm(algorithm);
132        }
133        collection
134    }
135
136    /// Add an algorithm to tracking
137    pub fn add_algorithm(&mut self, algorithm_type: AlgorithmType) {
138        self.metrics.insert(algorithm_type, MemoryMetrics::new(algorithm_type));
139    }
140
141    /// Remove an algorithm from tracking
142    pub fn remove_algorithm(&mut self, algorithm_type: AlgorithmType) -> Option<MemoryMetrics> {
143        self.metrics.remove(&algorithm_type)
144    }
145
146    /// Update memory usage for an algorithm
147    pub fn update_algorithm(&mut self, algorithm_type: AlgorithmType, current_bytes: usize) {
148        if let Some(metrics) = self.metrics.get_mut(&algorithm_type) {
149            metrics.update(current_bytes);
150            
151            // Update global peak
152            if current_bytes > self.global_peak {
153                self.global_peak = current_bytes;
154            }
155        } else {
156            // Auto-add algorithm if it doesn't exist
157            let mut metrics = MemoryMetrics::new(algorithm_type);
158            metrics.update(current_bytes);
159            self.metrics.insert(algorithm_type, metrics);
160            
161            if current_bytes > self.global_peak {
162                self.global_peak = current_bytes;
163            }
164        }
165    }
166
167    /// Get metrics for specific algorithm
168    pub fn get_metrics(&self, algorithm_type: AlgorithmType) -> Option<&MemoryMetrics> {
169        self.metrics.get(&algorithm_type)
170    }
171
172    /// Get mutable metrics for specific algorithm
173    pub fn get_metrics_mut(&mut self, algorithm_type: AlgorithmType) -> Option<&mut MemoryMetrics> {
174        self.metrics.get_mut(&algorithm_type)
175    }
176
177    /// Get all metrics
178    pub fn get_all_metrics(&self) -> &HashMap<AlgorithmType, MemoryMetrics> {
179        &self.metrics
180    }
181
182    /// Get metrics as a sorted vector for display
183    pub fn get_sorted_metrics(&self) -> Vec<(AlgorithmType, &MemoryMetrics)> {
184        let mut metrics: Vec<_> = self.metrics.iter().map(|(&alg, metrics)| (alg, metrics)).collect();
185        metrics.sort_by_key(|(alg, _)| alg.to_index());
186        metrics
187    }
188
189    /// Get global peak memory usage
190    pub fn get_global_peak(&self) -> usize {
191        self.global_peak
192    }
193
194    /// Get total current memory usage across all algorithms
195    pub fn get_total_current_usage(&self) -> usize {
196        self.metrics.values().map(|m| m.current_usage_bytes).sum()
197    }
198
199    /// Get total peak memory usage across all algorithms
200    pub fn get_total_peak_usage(&self) -> usize {
201        self.metrics.values().map(|m| m.peak_usage_bytes).sum()
202    }
203
204    /// Clear all metrics
205    pub fn clear(&mut self) {
206        self.metrics.clear();
207        self.global_peak = 0;
208    }
209
210    /// Reset all metrics to zero but keep tracking
211    pub fn reset_all(&mut self) {
212        for metrics in self.metrics.values_mut() {
213            metrics.reset();
214        }
215        self.global_peak = 0;
216    }
217
218    /// Enable or disable real-time updates
219    pub fn set_real_time_enabled(&mut self, enabled: bool) {
220        self.real_time_enabled = enabled;
221    }
222
223    /// Check if real-time updates are enabled
224    pub fn is_real_time_enabled(&self) -> bool {
225        self.real_time_enabled
226    }
227
228    /// Set update frequency for real-time updates
229    pub fn set_update_frequency(&mut self, frequency: std::time::Duration) {
230        self.update_frequency = frequency;
231    }
232
233    /// Get update frequency
234    pub fn get_update_frequency(&self) -> std::time::Duration {
235        self.update_frequency
236    }
237
238    /// Get algorithms that need updates (based on staleness)
239    pub fn get_stale_algorithms(&self, threshold: std::time::Duration) -> Vec<AlgorithmType> {
240        self.metrics
241            .iter()
242            .filter_map(|(&alg, metrics)| {
243                if metrics.is_stale(threshold) {
244                    Some(alg)
245                } else {
246                    None
247                }
248            })
249            .collect()
250    }
251
252    /// Get memory display values for all algorithms
253    pub fn get_memory_display_values(&self) -> Vec<(AlgorithmType, crate::models::display_mode::MemoryDisplayValue)> {
254        self.get_sorted_metrics()
255            .into_iter()
256            .map(|(alg, metrics)| {
257                let display_value = if metrics.current_usage_bytes > 0 {
258                    crate::models::display_mode::MemoryDisplayValue::Bytes(metrics.current_usage_bytes)
259                } else {
260                    crate::models::display_mode::MemoryDisplayValue::NotAvailable
261                };
262                (alg, display_value)
263            })
264            .collect()
265    }
266
267    /// Get collection age (time since creation)
268    pub fn age(&self) -> std::time::Duration {
269        self.start_time.elapsed()
270    }
271
272    /// Get memory statistics summary
273    pub fn get_statistics(&self) -> MemoryStatistics {
274        MemoryStatistics::from_collection(self)
275    }
276
277    /// Check if any algorithm is currently using memory
278    pub fn has_active_usage(&self) -> bool {
279        self.metrics.values().any(|m| m.current_usage_bytes > 0)
280    }
281
282    /// Get algorithm with highest current memory usage
283    pub fn get_highest_current_usage(&self) -> Option<(AlgorithmType, usize)> {
284        self.metrics
285            .iter()
286            .max_by_key(|(_, metrics)| metrics.current_usage_bytes)
287            .map(|(&alg, metrics)| (alg, metrics.current_usage_bytes))
288    }
289
290    /// Get algorithm with highest peak memory usage
291    pub fn get_highest_peak_usage(&self) -> Option<(AlgorithmType, usize)> {
292        self.metrics
293            .iter()
294            .max_by_key(|(_, metrics)| metrics.peak_usage_bytes)
295            .map(|(&alg, metrics)| (alg, metrics.peak_usage_bytes))
296    }
297}
298
299impl Default for MemoryMetricsCollection {
300    fn default() -> Self {
301        Self::new()
302    }
303}
304
305/// Memory usage statistics summary
306#[derive(Debug, Clone)]
307pub struct MemoryStatistics {
308    /// Total algorithms being tracked
309    pub algorithm_count: usize,
310    /// Total current memory usage
311    pub total_current: usize,
312    /// Total peak memory usage
313    pub total_peak: usize,
314    /// Global peak across all algorithms
315    pub global_peak: usize,
316    /// Average current usage per algorithm
317    pub average_current: usize,
318    /// Average peak usage per algorithm
319    pub average_peak: usize,
320    /// Most memory-efficient algorithm
321    pub most_efficient: Option<AlgorithmType>,
322    /// Least memory-efficient algorithm
323    pub least_efficient: Option<AlgorithmType>,
324}
325
326impl MemoryStatistics {
327    /// Generate statistics from a memory metrics collection
328    pub fn from_collection(collection: &MemoryMetricsCollection) -> Self {
329        let algorithm_count = collection.metrics.len();
330        let total_current = collection.get_total_current_usage();
331        let total_peak = collection.get_total_peak_usage();
332        let global_peak = collection.get_global_peak();
333
334        let average_current = if algorithm_count > 0 { total_current / algorithm_count } else { 0 };
335        let average_peak = if algorithm_count > 0 { total_peak / algorithm_count } else { 0 };
336
337        // Find most and least efficient algorithms
338        let mut most_efficient = None;
339        let mut least_efficient = None;
340        let mut best_efficiency = 0.0f64;
341        let mut worst_efficiency = f64::INFINITY;
342
343        for (&alg, metrics) in &collection.metrics {
344            let efficiency = metrics.efficiency_ratio();
345            if efficiency > best_efficiency {
346                best_efficiency = efficiency;
347                most_efficient = Some(alg);
348            }
349            if efficiency < worst_efficiency {
350                worst_efficiency = efficiency;
351                least_efficient = Some(alg);
352            }
353        }
354
355        Self {
356            algorithm_count,
357            total_current,
358            total_peak,
359            global_peak,
360            average_current,
361            average_peak,
362            most_efficient,
363            least_efficient,
364        }
365    }
366}
367
368#[cfg(test)]
369mod tests {
370    use super::*;
371
372    #[test]
373    fn test_memory_metrics_creation() {
374        let metrics = MemoryMetrics::new(AlgorithmType::BubbleSort);
375        
376        assert_eq!(metrics.algorithm_type, AlgorithmType::BubbleSort);
377        assert_eq!(metrics.current_usage_bytes, 0);
378        assert_eq!(metrics.peak_usage_bytes, 0);
379        assert!(metrics.age().as_millis() < 100); // Recently created
380    }
381
382    #[test]
383    fn test_memory_metrics_update() {
384        let mut metrics = MemoryMetrics::new(AlgorithmType::QuickSort);
385        
386        metrics.update(1024);
387        assert_eq!(metrics.current_usage_bytes, 1024);
388        assert_eq!(metrics.peak_usage_bytes, 1024);
389        
390        metrics.update(512);
391        assert_eq!(metrics.current_usage_bytes, 512);
392        assert_eq!(metrics.peak_usage_bytes, 1024); // Peak should remain
393        
394        metrics.update(2048);
395        assert_eq!(metrics.current_usage_bytes, 2048);
396        assert_eq!(metrics.peak_usage_bytes, 2048); // New peak
397    }
398
399    #[test]
400    fn test_memory_formatting() {
401        assert_eq!(MemoryMetrics::format_bytes(512), "512B");
402        assert_eq!(MemoryMetrics::format_bytes(1024), "1.0KB");
403        assert_eq!(MemoryMetrics::format_bytes(1536), "1.5KB");
404        assert_eq!(MemoryMetrics::format_bytes(1048576), "1.0MB");
405        
406        let metrics = MemoryMetrics::new(AlgorithmType::MergeSort);
407        assert_eq!(metrics.format_current(), "0B");
408    }
409
410    #[test]
411    fn test_memory_efficiency() {
412        let mut metrics = MemoryMetrics::new(AlgorithmType::HeapSort);
413        
414        metrics.update(1024);
415        assert_eq!(metrics.efficiency_ratio(), 1.0); // At peak
416        assert!(metrics.is_at_peak());
417        
418        metrics.update(512);
419        assert_eq!(metrics.efficiency_ratio(), 0.5); // Half of peak
420        assert!(!metrics.is_at_peak());
421    }
422
423    #[test]
424    fn test_memory_collection_creation() {
425        let collection = MemoryMetricsCollection::new();
426        
427        assert_eq!(collection.metrics.len(), 0);
428        assert_eq!(collection.global_peak, 0);
429        assert!(collection.is_real_time_enabled());
430        
431        let algorithms = vec![AlgorithmType::BubbleSort, AlgorithmType::QuickSort];
432        let collection_with_algs = MemoryMetricsCollection::with_algorithms(&algorithms);
433        assert_eq!(collection_with_algs.metrics.len(), 2);
434    }
435
436    #[test]
437    fn test_memory_collection_operations() {
438        let mut collection = MemoryMetricsCollection::new();
439        
440        // Add algorithm
441        collection.add_algorithm(AlgorithmType::BubbleSort);
442        assert!(collection.get_metrics(AlgorithmType::BubbleSort).is_some());
443        
444        // Update memory
445        collection.update_algorithm(AlgorithmType::BubbleSort, 1024);
446        let metrics = collection.get_metrics(AlgorithmType::BubbleSort).unwrap();
447        assert_eq!(metrics.current_usage_bytes, 1024);
448        assert_eq!(collection.get_global_peak(), 1024);
449        
450        // Remove algorithm
451        let removed = collection.remove_algorithm(AlgorithmType::BubbleSort);
452        assert!(removed.is_some());
453        assert!(collection.get_metrics(AlgorithmType::BubbleSort).is_none());
454    }
455
456    #[test]
457    fn test_memory_collection_totals() {
458        let mut collection = MemoryMetricsCollection::new();
459        
460        collection.update_algorithm(AlgorithmType::BubbleSort, 1024);
461        collection.update_algorithm(AlgorithmType::QuickSort, 2048);
462        
463        assert_eq!(collection.get_total_current_usage(), 3072);
464        assert_eq!(collection.get_total_peak_usage(), 3072);
465        
466        // Update one algorithm to lower value
467        collection.update_algorithm(AlgorithmType::BubbleSort, 512);
468        assert_eq!(collection.get_total_current_usage(), 2560);
469        assert_eq!(collection.get_total_peak_usage(), 3072); // Peak remains
470    }
471
472    #[test]
473    fn test_memory_statistics() {
474        let mut collection = MemoryMetricsCollection::new();
475        
476        collection.update_algorithm(AlgorithmType::BubbleSort, 1024);
477        collection.update_algorithm(AlgorithmType::QuickSort, 2048);
478        
479        let stats = collection.get_statistics();
480        assert_eq!(stats.algorithm_count, 2);
481        assert_eq!(stats.total_current, 3072);
482        assert_eq!(stats.average_current, 1536);
483    }
484
485    #[test]
486    fn test_memory_display_values() {
487        let mut collection = MemoryMetricsCollection::new();
488        
489        collection.update_algorithm(AlgorithmType::BubbleSort, 1024);
490        collection.add_algorithm(AlgorithmType::QuickSort); // 0 bytes
491        
492        let display_values = collection.get_memory_display_values();
493        assert_eq!(display_values.len(), 2);
494        
495        // Find BubbleSort entry
496        let bubble_entry = display_values.iter()
497            .find(|(alg, _)| *alg == AlgorithmType::BubbleSort)
498            .unwrap();
499        assert_eq!(bubble_entry.1, crate::models::display_mode::MemoryDisplayValue::Bytes(1024));
500        
501        // Find QuickSort entry (should be N/A due to 0 bytes)
502        let quick_entry = display_values.iter()
503            .find(|(alg, _)| *alg == AlgorithmType::QuickSort)
504            .unwrap();
505        assert_eq!(quick_entry.1, crate::models::display_mode::MemoryDisplayValue::NotAvailable);
506    }
507
508    #[test]
509    fn test_highest_usage_algorithms() {
510        let mut collection = MemoryMetricsCollection::new();
511        
512        collection.update_algorithm(AlgorithmType::BubbleSort, 1024);
513        collection.update_algorithm(AlgorithmType::QuickSort, 2048);
514        collection.update_algorithm(AlgorithmType::MergeSort, 512);
515        
516        let highest_current = collection.get_highest_current_usage();
517        assert_eq!(highest_current, Some((AlgorithmType::QuickSort, 2048)));
518        
519        let highest_peak = collection.get_highest_peak_usage();
520        assert_eq!(highest_peak, Some((AlgorithmType::QuickSort, 2048)));
521    }
522}