use serde::Serialize;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::time::{Duration, Instant};
#[derive(Default)]
pub struct PhaseTimer(AtomicU64);
impl PhaseTimer {
pub fn record(&self, elapsed: Duration) {
self.0
.fetch_add(elapsed.as_nanos() as u64, Ordering::Relaxed);
}
pub fn total(&self) -> Duration {
Duration::from_nanos(self.0.load(Ordering::Relaxed))
}
}
#[derive(Default)]
pub struct TimingCollector {
pub discover: PhaseTimer,
pub parse: PhaseTimer,
pub cfg: PhaseTimer,
pub metrics: PhaseTimer,
pub scoring: PhaseTimer,
pub aggregate: PhaseTimer,
pub report: PhaseTimer,
files_processed: AtomicUsize,
functions_processed: AtomicUsize,
}
impl TimingCollector {
pub fn record_file(&self) {
self.files_processed.fetch_add(1, Ordering::Relaxed);
}
pub fn record_function(&self) {
self.functions_processed.fetch_add(1, Ordering::Relaxed);
}
pub fn report(&self) -> TimingReport {
TimingReport {
phases: vec![
PhaseTiming::new("discover", self.discover.total()),
PhaseTiming::new("parse", self.parse.total()),
PhaseTiming::new("cfg", self.cfg.total()),
PhaseTiming::new("metrics", self.metrics.total()),
PhaseTiming::new("scoring", self.scoring.total()),
PhaseTiming::new("aggregate", self.aggregate.total()),
PhaseTiming::new("report", self.report.total()),
],
files_processed: self.files_processed.load(Ordering::Relaxed),
functions_processed: self.functions_processed.load(Ordering::Relaxed),
}
}
}
#[derive(Debug, Serialize)]
pub struct PhaseTiming {
pub phase: &'static str,
pub wall_ms: f64,
}
impl PhaseTiming {
fn new(phase: &'static str, elapsed: Duration) -> Self {
Self {
phase,
wall_ms: elapsed.as_secs_f64() * 1000.0,
}
}
}
#[derive(Debug, Serialize)]
pub struct TimingReport {
pub phases: Vec<PhaseTiming>,
pub files_processed: usize,
pub functions_processed: usize,
}
pub fn timed<T>(timer: Option<&PhaseTimer>, f: impl FnOnce() -> T) -> T {
match timer {
Some(timer) => {
let start = Instant::now();
let result = f();
timer.record(start.elapsed());
result
}
None => f(),
}
}