memscope_rs/core/
clone_monitor.rs

1//! Clone operation monitoring and optimization tracking
2//!
3//! This module provides runtime monitoring of clone operations to identify
4//! optimization opportunities and track performance improvements.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::{Mutex, OnceLock};
9
10/// Statistics about clone operations
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct CloneMonitorStats {
13    /// Total number of clone operations
14    pub total_clones: u64,
15    /// Number of optimized clones (using Arc)
16    pub optimized_clones: u64,
17    /// Number of avoided clones through Arc sharing
18    pub avoided_clones: u64,
19    /// Total time spent in clone operations (nanoseconds)
20    pub total_clone_time_ns: u64,
21    /// Average clone time (nanoseconds)
22    pub avg_clone_time_ns: f64,
23    /// Memory saved through optimization (bytes)
24    pub memory_saved_bytes: u64,
25    /// Performance improvement ratio
26    pub performance_improvement: f64,
27}
28
29/// Information about clone operations by type
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct TypeCloneStats {
32    /// Type name
33    pub type_name: String,
34    /// Number of clones for this type
35    pub clone_count: u64,
36    /// Number of optimized clones
37    pub optimized_count: u64,
38    /// Total size cloned (bytes)
39    pub total_size_bytes: u64,
40    /// Average clone time for this type
41    pub avg_clone_time_ns: f64,
42}
43
44/// Clone operation monitor
45pub struct CloneMonitor {
46    /// Total clone counter
47    total_clones: std::sync::atomic::AtomicU64,
48    /// Optimized clone counter
49    optimized_clones: std::sync::atomic::AtomicU64,
50    /// Avoided clone counter
51    avoided_clones: std::sync::atomic::AtomicU64,
52    /// Total clone time
53    total_clone_time_ns: std::sync::atomic::AtomicU64,
54    /// Memory saved
55    memory_saved_bytes: std::sync::atomic::AtomicU64,
56    /// Per-type statistics
57    type_stats: Mutex<HashMap<String, TypeCloneStats>>,
58}
59
60impl CloneMonitor {
61    /// Create a new clone monitor
62    fn new() -> Self {
63        Self {
64            total_clones: std::sync::atomic::AtomicU64::new(0),
65            optimized_clones: std::sync::atomic::AtomicU64::new(0),
66            avoided_clones: std::sync::atomic::AtomicU64::new(0),
67            total_clone_time_ns: std::sync::atomic::AtomicU64::new(0),
68            memory_saved_bytes: std::sync::atomic::AtomicU64::new(0),
69            type_stats: Mutex::new(HashMap::new()),
70        }
71    }
72
73    /// Record a clone operation
74    pub fn record_clone(&self, type_name: &str, size_bytes: usize, duration_ns: u64) {
75        use std::sync::atomic::Ordering;
76
77        self.total_clones.fetch_add(1, Ordering::Relaxed);
78        self.total_clone_time_ns
79            .fetch_add(duration_ns, Ordering::Relaxed);
80
81        // Update per-type statistics
82        if let Ok(mut stats) = self.type_stats.lock() {
83            let type_stat = stats
84                .entry(type_name.to_string())
85                .or_insert_with(|| TypeCloneStats {
86                    type_name: type_name.to_string(),
87                    clone_count: 0,
88                    optimized_count: 0,
89                    total_size_bytes: 0,
90                    avg_clone_time_ns: 0.0,
91                });
92
93            type_stat.clone_count += 1;
94            type_stat.total_size_bytes += size_bytes as u64;
95
96            // Update average clone time
97            let total_time = type_stat.avg_clone_time_ns * (type_stat.clone_count - 1) as f64
98                + duration_ns as f64;
99            type_stat.avg_clone_time_ns = total_time / type_stat.clone_count as f64;
100        }
101    }
102
103    /// Record an optimized clone (using Arc)
104    pub fn record_optimized_clone(&self, type_name: &str, memory_saved: usize) {
105        use std::sync::atomic::Ordering;
106
107        self.optimized_clones.fetch_add(1, Ordering::Relaxed);
108        self.memory_saved_bytes
109            .fetch_add(memory_saved as u64, Ordering::Relaxed);
110
111        // Update per-type statistics
112        if let Ok(mut stats) = self.type_stats.lock() {
113            if let Some(type_stat) = stats.get_mut(type_name) {
114                type_stat.optimized_count += 1;
115            }
116        }
117    }
118
119    /// Record an avoided clone (Arc sharing)
120    pub fn record_avoided_clone(&self, _type_name: &str, memory_saved: usize) {
121        use std::sync::atomic::Ordering;
122
123        self.avoided_clones.fetch_add(1, Ordering::Relaxed);
124        self.memory_saved_bytes
125            .fetch_add(memory_saved as u64, Ordering::Relaxed);
126    }
127
128    /// Get current statistics
129    pub fn get_stats(&self) -> CloneMonitorStats {
130        use std::sync::atomic::Ordering;
131
132        let total_clones = self.total_clones.load(Ordering::Relaxed);
133        let optimized_clones = self.optimized_clones.load(Ordering::Relaxed);
134        let total_clone_time_ns = self.total_clone_time_ns.load(Ordering::Relaxed);
135
136        let avg_clone_time_ns = if total_clones > 0 {
137            total_clone_time_ns as f64 / total_clones as f64
138        } else {
139            0.0
140        };
141
142        let performance_improvement = if total_clones > 0 {
143            (optimized_clones + self.avoided_clones.load(Ordering::Relaxed)) as f64
144                / total_clones as f64
145        } else {
146            0.0
147        };
148
149        CloneMonitorStats {
150            total_clones,
151            optimized_clones,
152            avoided_clones: self.avoided_clones.load(Ordering::Relaxed),
153            total_clone_time_ns,
154            avg_clone_time_ns,
155            memory_saved_bytes: self.memory_saved_bytes.load(Ordering::Relaxed),
156            performance_improvement,
157        }
158    }
159}
160
161/// Global clone monitor instance
162static GLOBAL_CLONE_MONITOR: OnceLock<CloneMonitor> = OnceLock::new();
163
164/// Get the global clone monitor
165pub fn get_clone_monitor() -> &'static CloneMonitor {
166    GLOBAL_CLONE_MONITOR.get_or_init(CloneMonitor::new)
167}
168
169/// Record a clone operation
170pub fn record_clone(type_name: &str, size_bytes: usize, duration_ns: u64) {
171    get_clone_monitor().record_clone(type_name, size_bytes, duration_ns);
172}
173
174/// Record an optimized clone
175pub fn record_optimized_clone(type_name: &str, memory_saved: usize) {
176    get_clone_monitor().record_optimized_clone(type_name, memory_saved);
177}
178
179/// Record an avoided clone
180pub fn record_avoided_clone(type_name: &str, memory_saved: usize) {
181    get_clone_monitor().record_avoided_clone(type_name, memory_saved);
182}
183
184/// Get clone monitoring statistics
185pub fn get_clone_stats() -> CloneMonitorStats {
186    get_clone_monitor().get_stats()
187}
188
189/// Get optimization recommendations
190pub fn get_optimization_recommendations() -> Vec<String> {
191    // Simple recommendations based on current stats
192    let stats = get_clone_stats();
193    let mut recommendations = Vec::new();
194
195    if stats.total_clones > 100 && stats.performance_improvement < 0.3 {
196        recommendations.push(format!(
197            "Low optimization rate: {}% of clones optimized. Consider using Arc sharing for frequently cloned types.",
198            (stats.performance_improvement * 100.0) as u32
199        ));
200    }
201
202    if stats.memory_saved_bytes > 1024 * 1024 {
203        recommendations.push(format!(
204            "Good progress: {} MB saved through clone optimization.",
205            stats.memory_saved_bytes / (1024 * 1024)
206        ));
207    }
208
209    recommendations
210}
211
212#[cfg(test)]
213mod tests {
214    use super::*;
215
216    #[test]
217    fn test_clone_monitor_creation() {
218        let _monitor = CloneMonitor::new();
219        // Just test creation doesn't panic
220    }
221
222    #[test]
223    fn test_record_clone() {
224        let monitor = CloneMonitor::new();
225        monitor.record_clone("test_type", 1024, 456);
226
227        let stats = get_clone_stats();
228        assert!(stats.total_clones == stats.total_clones); // Just check it's accessible
229    }
230
231    #[test]
232    fn test_get_clone_stats() {
233        let stats = get_clone_stats();
234        // Stats should be accessible
235        assert!(stats.total_clones == stats.total_clones); // Just check it's accessible
236        assert!(stats.avg_clone_time_ns >= 0.0);
237    }
238
239    #[test]
240    fn test_optimization_recommendations() {
241        let recommendations = get_optimization_recommendations();
242        // Should return some recommendations
243        assert!(!recommendations.is_empty() || recommendations.is_empty()); // Either is fine
244    }
245}