use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicUsize, Ordering};
pub struct MemoryProfiler;
static ALLOCATED: AtomicUsize = AtomicUsize::new(0);
static DEALLOCATED: AtomicUsize = AtomicUsize::new(0);
static PEAK_USAGE: AtomicUsize = AtomicUsize::new(0);
unsafe impl GlobalAlloc for MemoryProfiler {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = unsafe { System.alloc(layout) };
if !ptr.is_null() {
ALLOCATED.fetch_add(layout.size(), Ordering::SeqCst);
let current = ALLOCATED.load(Ordering::SeqCst) - DEALLOCATED.load(Ordering::SeqCst);
let mut peak = PEAK_USAGE.load(Ordering::SeqCst);
while current > peak {
match PEAK_USAGE.compare_exchange_weak(
peak,
current,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => break,
Err(actual) => peak = peak.max(actual),
}
}
}
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { System.dealloc(ptr, layout) };
DEALLOCATED.fetch_add(layout.size(), Ordering::SeqCst);
}
}
#[derive(Debug, Clone, Copy)]
pub struct MemoryStats {
pub allocated: usize,
pub deallocated: usize,
pub current_usage: usize,
pub peak_usage: usize,
}
impl MemoryStats {
pub fn current() -> Self {
let allocated = ALLOCATED.load(Ordering::Relaxed);
let deallocated = DEALLOCATED.load(Ordering::Relaxed);
let peak_usage = PEAK_USAGE.load(Ordering::Relaxed);
Self {
allocated,
deallocated,
current_usage: allocated - deallocated,
peak_usage,
}
}
pub fn reset() {
ALLOCATED.store(0, Ordering::Relaxed);
DEALLOCATED.store(0, Ordering::Relaxed);
PEAK_USAGE.store(0, Ordering::Relaxed);
}
}
#[macro_export]
macro_rules! profile_memory {
($name:expr, $code:block) => {{
let start_stats = $crate::profiling::MemoryStats::current();
let result = $code;
let end_stats = $crate::profiling::MemoryStats::current();
println!("{} - Memory Usage:", $name);
println!(
" Allocated: {} bytes",
end_stats.allocated - start_stats.allocated
);
println!(" Peak Usage: {} bytes", end_stats.peak_usage);
println!(" Current Usage: {} bytes", end_stats.current_usage);
result
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_stats() {
MemoryStats::reset();
let initial = MemoryStats::current();
let _vec: Vec<u8> = vec![0; 1024];
let after_alloc = MemoryStats::current();
assert!(after_alloc.allocated > initial.allocated);
assert!(after_alloc.current_usage > initial.current_usage);
}
}