use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
pub static ALLOC_COUNT: AtomicU64 = AtomicU64::new(0);
pub static ALLOC_BYTES: AtomicU64 = AtomicU64::new(0);
pub struct ProfilingGuard {
name: &'static str,
start: Instant,
}
impl ProfilingGuard {
pub fn new(name: &'static str) -> Self {
Self {
name,
start: Instant::now(),
}
}
pub fn elapsed(&self) -> Duration {
self.start.elapsed()
}
pub fn finish(self) -> ProfilingSummary {
let elapsed = self.elapsed();
let alloc_count_delta = ALLOC_COUNT.load(Ordering::Relaxed);
let alloc_bytes_delta = ALLOC_BYTES.load(Ordering::Relaxed);
tracing::debug!(
target: "amaters::profiling",
name = self.name,
elapsed_us = elapsed.as_micros() as u64,
alloc_count = alloc_count_delta,
alloc_bytes = alloc_bytes_delta,
"profiling guard finished"
);
let name = self.name;
std::mem::forget(self);
ProfilingSummary {
name,
elapsed,
alloc_count_delta,
alloc_bytes_delta,
}
}
}
impl Drop for ProfilingGuard {
fn drop(&mut self) {
tracing::debug!(
target: "amaters::profiling",
name = self.name,
elapsed_us = self.elapsed().as_micros() as u64,
"profiling guard dropped"
);
}
}
#[derive(Debug, Clone)]
pub struct ProfilingSummary {
pub name: &'static str,
pub elapsed: Duration,
pub alloc_count_delta: u64,
pub alloc_bytes_delta: u64,
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn test_profiling_guard_measures_elapsed() {
let guard = ProfilingGuard::new("test_op");
thread::sleep(Duration::from_millis(5));
let elapsed = guard.elapsed();
assert!(elapsed >= Duration::from_millis(1), "elapsed: {elapsed:?}");
drop(guard);
}
#[test]
fn test_profiling_summary_has_valid_duration() {
let guard = ProfilingGuard::new("summary_op");
thread::sleep(Duration::from_millis(2));
let summary = guard.finish();
assert_eq!(summary.name, "summary_op");
assert!(
summary.elapsed >= Duration::from_millis(1),
"elapsed: {:?}",
summary.elapsed
);
}
#[test]
fn test_profiling_guard_drop_does_not_panic() {
let guard = ProfilingGuard::new("drop_test");
drop(guard);
}
}