1use std::sync::atomic::{AtomicU64, Ordering};
7use std::sync::Arc;
8use std::time::{Duration, Instant};
9use tracing::{debug, info, warn};
10
11#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
13pub struct MemoryUsage {
14 pub heap_allocated: u64,
16 pub heap_deallocated: u64,
18 pub peak_memory: u64,
20 pub current_memory: u64,
22 pub allocation_count: u64,
24 pub deallocation_count: u64,
26 pub avg_allocation_size: f64,
28}
29
30impl Default for MemoryUsage {
31 fn default() -> Self {
32 Self {
33 heap_allocated: 0,
34 heap_deallocated: 0,
35 peak_memory: 0,
36 current_memory: 0,
37 allocation_count: 0,
38 deallocation_count: 0,
39 avg_allocation_size: 0.0,
40 }
41 }
42}
43
44impl MemoryUsage {
45 pub fn efficiency_ratio(&self) -> f64 {
47 if self.heap_allocated > 0 {
48 self.heap_deallocated as f64 / self.heap_allocated as f64
49 } else {
50 0.0
51 }
52 }
53
54 pub fn is_high_usage(&self, threshold_mb: u64) -> bool {
56 self.current_memory > threshold_mb * 1024 * 1024
57 }
58
59 pub fn current_memory_mb(&self) -> f64 {
61 self.current_memory as f64 / (1024.0 * 1024.0)
62 }
63
64 pub fn peak_memory_mb(&self) -> f64 {
66 self.peak_memory as f64 / (1024.0 * 1024.0)
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
72pub enum MemoryPressure {
73 Low,
75 Moderate,
77 High,
79 Critical,
81}
82
83impl MemoryPressure {
84 pub fn from_usage(current_mb: f64, peak_mb: f64) -> Self {
86 let usage_ratio = if peak_mb > 0.0 {
87 current_mb / peak_mb
88 } else {
89 0.0
90 };
91
92 match current_mb {
93 mb if mb > 1000.0 => Self::Critical, mb if mb > 500.0 => Self::High, mb if mb > 200.0 => Self::Moderate, _ if usage_ratio > 0.8 => Self::High, _ => Self::Low,
98 }
99 }
100
101 pub fn recommended_action(&self) -> &'static str {
103 match self {
104 Self::Low => "Normal operation",
105 Self::Moderate => "Start cache cleanup and reduce buffer sizes",
106 Self::High => "Aggressive cache cleanup and limit concurrent operations",
107 Self::Critical => "Emergency cleanup and pause non-essential operations",
108 }
109 }
110}
111
112#[derive(Debug)]
114pub struct MemoryTracker {
115 allocated: AtomicU64,
117 deallocated: AtomicU64,
119 peak_memory: AtomicU64,
121 allocation_count: AtomicU64,
123 deallocation_count: AtomicU64,
125 total_allocation_size: AtomicU64,
127 last_check: std::sync::Mutex<Instant>,
129 moderate_threshold: u64,
131 high_threshold: u64,
132 critical_threshold: u64,
133}
134
135impl Default for MemoryTracker {
136 fn default() -> Self {
137 Self::new()
138 }
139}
140
141impl MemoryTracker {
142 pub fn new() -> Self {
144 Self::with_thresholds(200, 500, 1000) }
146
147 pub fn with_thresholds(moderate: u64, high: u64, critical: u64) -> Self {
149 Self {
150 allocated: AtomicU64::new(0),
151 deallocated: AtomicU64::new(0),
152 peak_memory: AtomicU64::new(0),
153 allocation_count: AtomicU64::new(0),
154 deallocation_count: AtomicU64::new(0),
155 total_allocation_size: AtomicU64::new(0),
156 last_check: std::sync::Mutex::new(Instant::now()),
157 moderate_threshold: moderate,
158 high_threshold: high,
159 critical_threshold: critical,
160 }
161 }
162
163 pub fn record_allocation(&self, size: usize) {
165 let size_u64 = size as u64;
166
167 self.allocated.fetch_add(size_u64, Ordering::Relaxed);
168 self.allocation_count.fetch_add(1, Ordering::Relaxed);
169 self.total_allocation_size
170 .fetch_add(size_u64, Ordering::Relaxed);
171
172 let current = self.current_memory();
174 let mut peak = self.peak_memory.load(Ordering::Relaxed);
175 while current > peak {
176 match self.peak_memory.compare_exchange_weak(
177 peak,
178 current,
179 Ordering::Relaxed,
180 Ordering::Relaxed,
181 ) {
182 Ok(_) => break,
183 Err(new_peak) => peak = new_peak,
184 }
185 }
186
187 if size > 10 * 1024 * 1024 {
189 debug!(
191 "Large allocation: {:.2} MB",
192 size as f64 / (1024.0 * 1024.0)
193 );
194 }
195 }
196
197 pub fn record_deallocation(&self, size: usize) {
199 let size_u64 = size as u64;
200
201 self.deallocated.fetch_add(size_u64, Ordering::Relaxed);
202 self.deallocation_count.fetch_add(1, Ordering::Relaxed);
203 }
204
205 pub fn get_usage(&self) -> MemoryUsage {
207 let allocated = self.allocated.load(Ordering::Relaxed);
208 let deallocated = self.deallocated.load(Ordering::Relaxed);
209 let allocation_count = self.allocation_count.load(Ordering::Relaxed);
210 let total_allocation_size = self.total_allocation_size.load(Ordering::Relaxed);
211
212 MemoryUsage {
213 heap_allocated: allocated,
214 heap_deallocated: deallocated,
215 peak_memory: self.peak_memory.load(Ordering::Relaxed),
216 current_memory: allocated.saturating_sub(deallocated),
217 allocation_count,
218 deallocation_count: self.deallocation_count.load(Ordering::Relaxed),
219 avg_allocation_size: if allocation_count > 0 {
220 total_allocation_size as f64 / allocation_count as f64
221 } else {
222 0.0
223 },
224 }
225 }
226
227 pub fn current_memory(&self) -> u64 {
229 let allocated = self.allocated.load(Ordering::Relaxed);
230 let deallocated = self.deallocated.load(Ordering::Relaxed);
231 allocated.saturating_sub(deallocated)
232 }
233
234 pub fn memory_pressure(&self) -> MemoryPressure {
236 let usage = self.get_usage();
237 let current_mb = usage.current_memory_mb();
238
239 match current_mb as u64 {
241 mb if mb >= self.critical_threshold => MemoryPressure::Critical,
242 mb if mb >= self.high_threshold => MemoryPressure::High,
243 mb if mb >= self.moderate_threshold => MemoryPressure::Moderate,
244 _ => MemoryPressure::Low,
245 }
246 }
247
248 pub fn check_memory_pressure(&self) -> MemoryPressure {
250 let pressure = self.memory_pressure();
251 let usage = self.get_usage();
252
253 if let Ok(mut last_check) = self.last_check.lock() {
255 if last_check.elapsed() > Duration::from_secs(30) {
256 match pressure {
257 MemoryPressure::Critical => {
258 warn!(
259 "Critical memory pressure: {:.2} MB current, {:.2} MB peak - {}",
260 usage.current_memory_mb(),
261 usage.peak_memory_mb(),
262 pressure.recommended_action()
263 );
264 }
265 MemoryPressure::High => {
266 warn!(
267 "High memory pressure: {:.2} MB current, {:.2} MB peak - {}",
268 usage.current_memory_mb(),
269 usage.peak_memory_mb(),
270 pressure.recommended_action()
271 );
272 }
273 MemoryPressure::Moderate => {
274 info!(
275 "Moderate memory usage: {:.2} MB current, {:.2} MB peak",
276 usage.current_memory_mb(),
277 usage.peak_memory_mb()
278 );
279 }
280 MemoryPressure::Low => {
281 debug!(
282 "Low memory usage: {:.2} MB current, {:.2} MB peak",
283 usage.current_memory_mb(),
284 usage.peak_memory_mb()
285 );
286 }
287 }
288 *last_check = Instant::now();
289 }
290 }
291
292 pressure
293 }
294
295 pub fn reset(&self) {
297 self.allocated.store(0, Ordering::Relaxed);
298 self.deallocated.store(0, Ordering::Relaxed);
299 self.peak_memory.store(0, Ordering::Relaxed);
300 self.allocation_count.store(0, Ordering::Relaxed);
301 self.deallocation_count.store(0, Ordering::Relaxed);
302 self.total_allocation_size.store(0, Ordering::Relaxed);
303 if let Ok(mut last_check) = self.last_check.lock() {
304 *last_check = Instant::now();
305 }
306 }
307
308 pub fn efficiency_metrics(&self) -> MemoryEfficiencyMetrics {
310 let usage = self.get_usage();
311
312 MemoryEfficiencyMetrics {
313 efficiency_ratio: usage.efficiency_ratio(),
314 fragmentation_ratio: self.calculate_fragmentation_ratio(),
315 allocation_rate: self.calculate_allocation_rate(),
316 pressure_level: self.memory_pressure(),
317 recommendations: self.get_recommendations(),
318 }
319 }
320
321 fn calculate_fragmentation_ratio(&self) -> f64 {
323 let allocation_count = self.allocation_count.load(Ordering::Relaxed);
324 let deallocation_count = self.deallocation_count.load(Ordering::Relaxed);
325
326 if allocation_count > 0 {
327 let active_allocations = allocation_count.saturating_sub(deallocation_count);
328 active_allocations as f64 / allocation_count as f64
329 } else {
330 0.0
331 }
332 }
333
334 fn calculate_allocation_rate(&self) -> f64 {
336 if let Ok(last_check) = self.last_check.lock() {
337 let elapsed = last_check.elapsed().as_secs_f64();
338
339 if elapsed > 0.0 {
340 let allocation_count = self.allocation_count.load(Ordering::Relaxed);
341 allocation_count as f64 / elapsed
342 } else {
343 0.0
344 }
345 } else {
346 0.0 }
348 }
349
350 fn get_recommendations(&self) -> Vec<String> {
352 let mut recommendations = Vec::new();
353 let usage = self.get_usage();
354 let pressure = self.memory_pressure();
355
356 match pressure {
357 MemoryPressure::Critical => {
358 recommendations.push("Immediately reduce concurrent operations".to_string());
359 recommendations.push("Clear all non-essential caches".to_string());
360 recommendations.push("Consider reducing buffer sizes".to_string());
361 }
362 MemoryPressure::High => {
363 recommendations.push("Reduce concurrent chunk downloads".to_string());
364 recommendations.push("Enable aggressive cache cleanup".to_string());
365 recommendations.push("Consider smaller buffer sizes".to_string());
366 }
367 MemoryPressure::Moderate => {
368 recommendations.push("Enable periodic cache cleanup".to_string());
369 recommendations.push("Monitor allocation patterns".to_string());
370 }
371 MemoryPressure::Low => {
372 if usage.efficiency_ratio() < 0.8 {
373 recommendations.push("Consider optimizing memory reuse".to_string());
374 }
375 }
376 }
377
378 if usage.avg_allocation_size > 1024.0 * 1024.0 {
379 recommendations.push(
381 "Large average allocation size detected - consider buffer pooling".to_string(),
382 );
383 }
384
385 recommendations
386 }
387}
388
389#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
391pub struct MemoryEfficiencyMetrics {
392 pub efficiency_ratio: f64,
394 pub fragmentation_ratio: f64,
396 pub allocation_rate: f64,
398 pub pressure_level: MemoryPressure,
400 pub recommendations: Vec<String>,
402}
403
404static GLOBAL_MEMORY_TRACKER: once_cell::sync::Lazy<Arc<MemoryTracker>> =
406 once_cell::sync::Lazy::new(|| Arc::new(MemoryTracker::new()));
407
408pub fn global_memory_tracker() -> Arc<MemoryTracker> {
410 GLOBAL_MEMORY_TRACKER.clone()
411}
412
413pub fn record_allocation(size: usize) {
415 GLOBAL_MEMORY_TRACKER.record_allocation(size);
416}
417
418pub fn record_deallocation(size: usize) {
420 GLOBAL_MEMORY_TRACKER.record_deallocation(size);
421}
422
423pub fn global_memory_usage() -> MemoryUsage {
425 GLOBAL_MEMORY_TRACKER.get_usage()
426}
427
428pub fn global_memory_pressure() -> MemoryPressure {
430 GLOBAL_MEMORY_TRACKER.memory_pressure()
431}
432
433pub fn check_global_memory_pressure() -> MemoryPressure {
435 GLOBAL_MEMORY_TRACKER.check_memory_pressure()
436}
437
438#[derive(Debug)]
443pub struct TrackingAllocator;
444
445unsafe impl std::alloc::GlobalAlloc for TrackingAllocator {
446 unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 {
447 let ptr = mimalloc::MiMalloc.alloc(layout);
448 if !ptr.is_null() {
449 record_allocation(layout.size());
450 }
451 ptr
452 }
453
454 unsafe fn dealloc(&self, ptr: *mut u8, layout: std::alloc::Layout) {
455 record_deallocation(layout.size());
456 mimalloc::MiMalloc.dealloc(ptr, layout);
457 }
458
459 unsafe fn alloc_zeroed(&self, layout: std::alloc::Layout) -> *mut u8 {
460 let ptr = mimalloc::MiMalloc.alloc_zeroed(layout);
461 if !ptr.is_null() {
462 record_allocation(layout.size());
463 }
464 ptr
465 }
466
467 unsafe fn realloc(&self, ptr: *mut u8, layout: std::alloc::Layout, new_size: usize) -> *mut u8 {
468 let new_ptr = mimalloc::MiMalloc.realloc(ptr, layout, new_size);
469 if !new_ptr.is_null() {
470 record_deallocation(layout.size());
472 record_allocation(new_size);
473 }
474 new_ptr
475 }
476}
477
478pub static TRACKING_ALLOCATOR: TrackingAllocator = TrackingAllocator;
486
487#[cfg(test)]
488mod tests {
489 use super::*;
490
491 #[test]
492 fn test_memory_tracker_creation() {
493 let tracker = MemoryTracker::new();
494 let usage = tracker.get_usage();
495
496 assert_eq!(usage.heap_allocated, 0);
497 assert_eq!(usage.heap_deallocated, 0);
498 assert_eq!(usage.current_memory, 0);
499 assert_eq!(usage.allocation_count, 0);
500 }
501
502 #[test]
503 fn test_memory_allocation_tracking() {
504 let tracker = MemoryTracker::new();
505
506 tracker.record_allocation(1024);
507 tracker.record_allocation(2048);
508
509 let usage = tracker.get_usage();
510 assert_eq!(usage.heap_allocated, 3072);
511 assert_eq!(usage.allocation_count, 2);
512 assert_eq!(usage.current_memory, 3072);
513 assert_eq!(usage.avg_allocation_size, 1536.0);
514 }
515
516 #[test]
517 fn test_memory_deallocation_tracking() {
518 let tracker = MemoryTracker::new();
519
520 tracker.record_allocation(2048);
521 tracker.record_deallocation(1024);
522
523 let usage = tracker.get_usage();
524 assert_eq!(usage.heap_allocated, 2048);
525 assert_eq!(usage.heap_deallocated, 1024);
526 assert_eq!(usage.current_memory, 1024);
527 assert_eq!(usage.deallocation_count, 1);
528 }
529
530 #[test]
531 fn test_peak_memory_tracking() {
532 let tracker = MemoryTracker::new();
533
534 tracker.record_allocation(1024);
535 tracker.record_allocation(2048);
536 tracker.record_deallocation(1024);
537
538 let usage = tracker.get_usage();
539 assert_eq!(usage.peak_memory, 3072);
540 assert_eq!(usage.current_memory, 2048);
541 }
542
543 #[test]
544 fn test_memory_pressure_levels() {
545 assert_eq!(MemoryPressure::from_usage(50.0, 100.0), MemoryPressure::Low);
546 assert_eq!(
547 MemoryPressure::from_usage(250.0, 300.0),
548 MemoryPressure::Moderate
549 );
550 assert_eq!(
551 MemoryPressure::from_usage(600.0, 700.0),
552 MemoryPressure::High
553 );
554 assert_eq!(
555 MemoryPressure::from_usage(1200.0, 1300.0),
556 MemoryPressure::Critical
557 );
558 }
559
560 #[test]
561 fn test_memory_efficiency_ratio() {
562 let usage = MemoryUsage {
563 heap_allocated: 1000,
564 heap_deallocated: 800,
565 ..Default::default()
566 };
567
568 assert_eq!(usage.efficiency_ratio(), 0.8);
569 }
570
571 #[test]
572 fn test_global_memory_tracker() {
573 let tracker1 = global_memory_tracker();
574 let tracker2 = global_memory_tracker();
575
576 assert!(Arc::ptr_eq(&tracker1, &tracker2));
578 }
579
580 #[test]
581 fn test_memory_usage_mb_conversion() {
582 let usage = MemoryUsage {
583 current_memory: 1024 * 1024, peak_memory: 2 * 1024 * 1024, ..Default::default()
586 };
587
588 assert_eq!(usage.current_memory_mb(), 1.0);
589 assert_eq!(usage.peak_memory_mb(), 2.0);
590 }
591
592 #[test]
593 fn test_memory_tracker_reset() {
594 let tracker = MemoryTracker::new();
595
596 tracker.record_allocation(1024);
597 tracker.record_deallocation(512);
598
599 let usage_before = tracker.get_usage();
600 assert!(usage_before.heap_allocated > 0);
601
602 tracker.reset();
603
604 let usage_after = tracker.get_usage();
605 assert_eq!(usage_after.heap_allocated, 0);
606 assert_eq!(usage_after.heap_deallocated, 0);
607 assert_eq!(usage_after.allocation_count, 0);
608 }
609}