memscope_rs/core/
clone_monitor.rs1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::{Mutex, OnceLock};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct CloneMonitorStats {
13 pub total_clones: u64,
15 pub optimized_clones: u64,
17 pub avoided_clones: u64,
19 pub total_clone_time_ns: u64,
21 pub avg_clone_time_ns: f64,
23 pub memory_saved_bytes: u64,
25 pub performance_improvement: f64,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct TypeCloneStats {
32 pub type_name: String,
34 pub clone_count: u64,
36 pub optimized_count: u64,
38 pub total_size_bytes: u64,
40 pub avg_clone_time_ns: f64,
42}
43
44pub struct CloneMonitor {
46 total_clones: std::sync::atomic::AtomicU64,
48 optimized_clones: std::sync::atomic::AtomicU64,
50 avoided_clones: std::sync::atomic::AtomicU64,
52 total_clone_time_ns: std::sync::atomic::AtomicU64,
54 memory_saved_bytes: std::sync::atomic::AtomicU64,
56 type_stats: Mutex<HashMap<String, TypeCloneStats>>,
58}
59
60impl CloneMonitor {
61 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 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 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 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 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 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 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 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
161static GLOBAL_CLONE_MONITOR: OnceLock<CloneMonitor> = OnceLock::new();
163
164pub fn get_clone_monitor() -> &'static CloneMonitor {
166 GLOBAL_CLONE_MONITOR.get_or_init(CloneMonitor::new)
167}
168
169pub 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
174pub fn record_optimized_clone(type_name: &str, memory_saved: usize) {
176 get_clone_monitor().record_optimized_clone(type_name, memory_saved);
177}
178
179pub fn record_avoided_clone(type_name: &str, memory_saved: usize) {
181 get_clone_monitor().record_avoided_clone(type_name, memory_saved);
182}
183
184pub fn get_clone_stats() -> CloneMonitorStats {
186 get_clone_monitor().get_stats()
187}
188
189pub fn get_optimization_recommendations() -> Vec<String> {
191 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 }
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); }
230
231 #[test]
232 fn test_get_clone_stats() {
233 let stats = get_clone_stats();
234 assert!(stats.total_clones == stats.total_clones); assert!(stats.avg_clone_time_ns >= 0.0);
237 }
238
239 #[test]
240 fn test_optimization_recommendations() {
241 let recommendations = get_optimization_recommendations();
242 assert!(!recommendations.is_empty() || recommendations.is_empty()); }
245}