memscope_rs/async_memory/
profile.rs1use std::time::{Duration, Instant};
7use crate::async_memory::TaskId;
10
11#[derive(Debug, Clone)]
13pub struct TaskMemoryProfile {
14 pub task_id: TaskId,
16 pub created_at: Instant,
18 pub completed_at: Option<Instant>,
20 pub total_allocated: u64,
22 pub current_usage: u64,
24 pub peak_usage: u64,
26 pub allocation_count: u64,
28 pub deallocation_count: u64,
30 pub average_allocation_size: f64,
32}
33
34impl TaskMemoryProfile {
35 pub fn new(task_id: TaskId) -> Self {
37 Self {
38 task_id,
39 created_at: Instant::now(),
40 completed_at: None,
41 total_allocated: 0,
42 current_usage: 0,
43 peak_usage: 0,
44 allocation_count: 0,
45 deallocation_count: 0,
46 average_allocation_size: 0.0,
47 }
48 }
49
50 pub fn record_allocation(&mut self, size: u64) {
52 self.total_allocated += size;
53 self.current_usage += size;
54 self.allocation_count += 1;
55
56 if self.current_usage > self.peak_usage {
58 self.peak_usage = self.current_usage;
59 }
60
61 self.average_allocation_size = self.total_allocated as f64 / self.allocation_count as f64;
63 }
64
65 pub fn record_deallocation(&mut self, size: u64) {
67 self.current_usage = self.current_usage.saturating_sub(size);
68 self.deallocation_count += 1;
69 }
70
71 pub fn mark_completed(&mut self) {
73 self.completed_at = Some(Instant::now());
74 }
75
76 pub fn is_completed(&self) -> bool {
78 self.completed_at.is_some()
79 }
80
81 pub fn lifetime(&self) -> Duration {
83 let end = self.completed_at.unwrap_or_else(Instant::now);
84 end.duration_since(self.created_at)
85 }
86
87 pub fn memory_efficiency(&self) -> f64 {
89 if self.total_allocated == 0 {
90 1.0
91 } else {
92 let deallocated = self.total_allocated - self.current_usage;
93 deallocated as f64 / self.total_allocated as f64
94 }
95 }
96
97 pub fn has_potential_leak(&self) -> bool {
99 self.is_completed() && self.current_usage > 0
100 }
101}
102
103#[derive(Debug, Clone)]
105pub struct TaskPerformanceMetrics {
106 pub execution_time: Duration,
108 pub allocation_rate: f64,
110 pub efficiency_ratio: f64,
112 pub peak_memory_mb: f64,
114 pub avg_allocation_size: f64,
116}
117
118impl TaskPerformanceMetrics {
119 pub fn from_profile(profile: &TaskMemoryProfile) -> Self {
121 let lifetime = profile.lifetime();
122 let lifetime_secs = lifetime.as_secs_f64();
123
124 let allocation_rate = if lifetime_secs > 0.0 {
125 profile.total_allocated as f64 / lifetime_secs
126 } else {
127 0.0
128 };
129
130 Self {
131 execution_time: lifetime,
132 allocation_rate,
133 efficiency_ratio: profile.memory_efficiency(),
134 peak_memory_mb: profile.peak_usage as f64 / 1_048_576.0, avg_allocation_size: profile.average_allocation_size,
136 }
137 }
138
139 pub fn performance_rating(&self) -> f64 {
141 let efficiency_score = self.efficiency_ratio;
143 let speed_score = if self.allocation_rate < 1_000_000.0 {
144 1.0
145 } else {
146 0.5
147 }; let memory_score = if self.peak_memory_mb < 10.0 { 1.0 } else { 0.7 }; (efficiency_score + speed_score + memory_score) / 3.0
151 }
152}
153
154#[derive(Debug, Clone)]
156pub struct AggregatedTaskStats {
157 pub total_tasks: usize,
159 pub completed_tasks: usize,
161 pub total_memory_allocated: u64,
163 pub current_memory_usage: u64,
165 pub peak_memory_usage: u64,
167 pub average_lifetime: Duration,
169 pub overall_efficiency: f64,
171 pub potential_leaks: usize,
173}
174
175impl AggregatedTaskStats {
176 pub fn new() -> Self {
178 Self {
179 total_tasks: 0,
180 completed_tasks: 0,
181 total_memory_allocated: 0,
182 current_memory_usage: 0,
183 peak_memory_usage: 0,
184 average_lifetime: Duration::ZERO,
185 overall_efficiency: 1.0,
186 potential_leaks: 0,
187 }
188 }
189
190 pub fn add_task(&mut self, profile: &TaskMemoryProfile) {
192 self.total_tasks += 1;
193
194 if profile.is_completed() {
195 self.completed_tasks += 1;
196 }
197
198 self.total_memory_allocated += profile.total_allocated;
199 self.current_memory_usage += profile.current_usage;
200
201 if profile.peak_usage > self.peak_memory_usage {
202 self.peak_memory_usage = profile.peak_usage;
203 }
204
205 if profile.has_potential_leak() {
206 self.potential_leaks += 1;
207 }
208
209 if self.completed_tasks > 0 {
211 self.overall_efficiency = if self.total_memory_allocated > 0 {
212 let total_deallocated = self.total_memory_allocated - self.current_memory_usage;
213 total_deallocated as f64 / self.total_memory_allocated as f64
214 } else {
215 1.0
216 };
217 }
218 }
219
220 pub fn memory_summary(&self) -> String {
222 format!(
223 "Tasks: {} ({}% complete), Memory: {:.1}MB allocated, {:.1}MB current, {:.1}% efficiency",
224 self.total_tasks,
225 if self.total_tasks > 0 { self.completed_tasks * 100 / self.total_tasks } else { 0 },
226 self.total_memory_allocated as f64 / 1_048_576.0,
227 self.current_memory_usage as f64 / 1_048_576.0,
228 self.overall_efficiency * 100.0
229 )
230 }
231}
232
233impl Default for AggregatedTaskStats {
234 fn default() -> Self {
235 Self::new()
236 }
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242 use std::thread;
243 use std::time::Duration;
244
245 #[test]
246 fn test_task_memory_profile_basic() {
247 let mut profile = TaskMemoryProfile::new(12345);
248
249 assert_eq!(profile.task_id, 12345);
250 assert!(!profile.is_completed());
251 assert_eq!(profile.current_usage, 0);
252 assert_eq!(profile.total_allocated, 0);
253
254 profile.record_allocation(1024);
256 assert_eq!(profile.current_usage, 1024);
257 assert_eq!(profile.total_allocated, 1024);
258 assert_eq!(profile.peak_usage, 1024);
259 assert_eq!(profile.allocation_count, 1);
260
261 profile.record_allocation(2048);
262 assert_eq!(profile.current_usage, 3072);
263 assert_eq!(profile.total_allocated, 3072);
264 assert_eq!(profile.peak_usage, 3072);
265 assert_eq!(profile.allocation_count, 2);
266
267 profile.record_deallocation(1024);
269 assert_eq!(profile.current_usage, 2048);
270 assert_eq!(profile.total_allocated, 3072); assert_eq!(profile.peak_usage, 3072); assert_eq!(profile.deallocation_count, 1);
273 }
274
275 #[test]
276 fn test_memory_efficiency_calculation() {
277 let mut profile = TaskMemoryProfile::new(1);
278
279 profile.record_allocation(1000);
281 profile.record_deallocation(1000);
282 assert_eq!(profile.memory_efficiency(), 1.0);
283
284 profile.record_allocation(1000); assert_eq!(profile.memory_efficiency(), 0.5);
287
288 let mut profile2 = TaskMemoryProfile::new(2);
290 profile2.record_allocation(1000);
291 assert_eq!(profile2.memory_efficiency(), 0.0);
292 }
293
294 #[test]
295 fn test_memory_leak_detection() {
296 let mut profile = TaskMemoryProfile::new(1);
297
298 profile.record_allocation(1000);
300 assert!(!profile.has_potential_leak());
301
302 profile.record_deallocation(1000);
304 profile.mark_completed();
305 assert!(!profile.has_potential_leak());
306
307 let mut profile2 = TaskMemoryProfile::new(2);
309 profile2.record_allocation(1000);
310 profile2.mark_completed();
311 assert!(profile2.has_potential_leak());
312 }
313
314 #[test]
315 fn test_task_lifetime() {
316 let profile = TaskMemoryProfile::new(1);
317 thread::sleep(Duration::from_millis(10));
318
319 let lifetime = profile.lifetime();
320 assert!(lifetime >= Duration::from_millis(10));
321 assert!(lifetime < Duration::from_millis(100)); }
323
324 #[test]
325 fn test_performance_metrics() {
326 let mut profile = TaskMemoryProfile::new(1);
327
328 profile.record_allocation(1_000_000); profile.record_allocation(500_000); profile.record_deallocation(500_000); thread::sleep(Duration::from_millis(10));
334 profile.mark_completed();
335
336 let metrics = TaskPerformanceMetrics::from_profile(&profile);
337
338 assert!(metrics.execution_time >= Duration::from_millis(10));
339 assert!(metrics.allocation_rate > 0.0);
340 assert_eq!(metrics.efficiency_ratio, profile.memory_efficiency());
341 assert!((metrics.peak_memory_mb - 1.4305114746).abs() < 0.0001); let rating = metrics.performance_rating();
344 assert!((0.0..=1.0).contains(&rating));
345 }
346
347 #[test]
348 fn test_aggregated_stats() {
349 let mut stats = AggregatedTaskStats::new();
350 assert_eq!(stats.total_tasks, 0);
351 assert_eq!(stats.completed_tasks, 0);
352
353 let mut profile1 = TaskMemoryProfile::new(1);
355 profile1.record_allocation(1000);
356 profile1.record_deallocation(500);
357 profile1.mark_completed();
358 stats.add_task(&profile1);
359
360 assert_eq!(stats.total_tasks, 1);
361 assert_eq!(stats.completed_tasks, 1);
362 assert_eq!(stats.total_memory_allocated, 1000);
363 assert_eq!(stats.current_memory_usage, 500);
364 assert_eq!(stats.overall_efficiency, 0.5);
365
366 let mut profile2 = TaskMemoryProfile::new(2);
368 profile2.record_allocation(2000);
369 stats.add_task(&profile2);
370
371 assert_eq!(stats.total_tasks, 2);
372 assert_eq!(stats.completed_tasks, 1);
373 assert_eq!(stats.total_memory_allocated, 3000);
374 assert_eq!(stats.current_memory_usage, 2500);
375
376 let summary = stats.memory_summary();
378 assert!(summary.contains("Tasks: 2"));
379 assert!(summary.contains("50% complete"));
380 }
381
382 #[test]
383 fn test_average_allocation_size() {
384 let mut profile = TaskMemoryProfile::new(1);
385
386 profile.record_allocation(100);
387 assert_eq!(profile.average_allocation_size, 100.0);
388
389 profile.record_allocation(200);
390 assert_eq!(profile.average_allocation_size, 150.0); profile.record_allocation(300);
393 assert_eq!(profile.average_allocation_size, 200.0); }
395}