use std::{
fmt::Display,
sync::{Arc, Mutex},
time::{Duration, Instant},
};
use paralegal_spdg::utils::TruncatedHumanTime;
use trait_enum::DerefMut;
#[derive(Debug, Clone, Copy, strum::AsRefStr, PartialEq, Eq, enum_map::Enum)]
pub enum TimedStat {
Rustc,
Flowistry,
Conversion,
Serialization,
MirEmission,
}
struct StatsInner {
timed: enum_map::EnumMap<TimedStat, Option<Duration>>,
started: Instant,
}
impl Default for StatsInner {
fn default() -> Self {
Self {
started: std::time::Instant::now(),
timed: Default::default(),
}
}
}
impl StatsInner {
fn record_timed(&mut self, stat: TimedStat, duration: Duration) {
*self.timed[stat].get_or_insert(Duration::ZERO) += duration
}
}
#[derive(Clone)]
pub struct Stats(Arc<Mutex<StatsInner>>);
impl Stats {
fn inner_mut(&self) -> impl DerefMut<Target = StatsInner> + '_ {
self.0.as_ref().lock().unwrap()
}
pub fn record_timed(&self, stat: TimedStat, duration: Duration) {
self.inner_mut().record_timed(stat, duration)
}
pub fn get_timed(&self, stat: TimedStat) -> Duration {
self.0.lock().unwrap().timed[stat].unwrap_or(Duration::ZERO)
}
pub fn elapsed(&self) -> Duration {
self.0.lock().unwrap().started.elapsed()
}
pub fn measure<R>(&self, stat: TimedStat, target: impl FnOnce() -> R) -> R {
let start = Instant::now();
let r = target();
self.record_timed(stat, start.elapsed());
r
}
}
impl Default for Stats {
fn default() -> Self {
Self(Arc::new(Mutex::new(Default::default())))
}
}
impl Display for Stats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let borrow = self.0.as_ref().lock().unwrap();
for (s, dur) in borrow.timed {
if let Some(dur) = dur {
write!(f, "{}: {} ", s.as_ref(), TruncatedHumanTime::from(dur))?;
}
}
write!(
f,
"current runtime: {}",
TruncatedHumanTime::from(borrow.started.elapsed())
)?;
Ok(())
}
}