use std::time;
#[derive(Debug)]
pub struct TotalTimeStats {
current: time::Duration,
total: time::Duration,
}
pub trait Reporter {
fn handle_stats(&mut self, stats: &mut TotalTimeStats);
}
impl<F> Reporter for F
where
F: for<'a> Fn(&'a mut TotalTimeStats),
{
fn handle_stats(&mut self, stats: &mut TotalTimeStats) {
(self as &mut F)(stats);
}
}
#[derive(Debug)]
pub struct TotalTimeProfiler<Reporter> {
reporter: Reporter,
start: time::Instant,
stats: TotalTimeStats,
}
impl<F> TotalTimeProfiler<F>
where
F: for<'a> Fn(&'a mut TotalTimeStats),
{
pub fn new(f: F) -> Self {
Self {
stats: TotalTimeStats {
current: time::Duration::default(),
total: time::Duration::default(),
},
start: time::Instant::now(),
reporter: f,
}
}
}
impl<F> TotalTimeProfiler<PeriodicReporter<F>>
where
F: Fn(),
{
pub fn periodically_millis(millis: u64, f: F) -> Self {
Self::periodically(time::Duration::from_millis(millis), f)
}
pub fn periodically(period: time::Duration, f: F) -> Self {
Self {
stats: TotalTimeStats {
current: time::Duration::default(),
total: time::Duration::default(),
},
start: time::Instant::now(),
reporter: PeriodicReporter::new_millis(period, f),
}
}
}
pub struct PeriodicReporter<F> {
threshold: time::Duration,
f: F,
}
impl<F> PeriodicReporter<F>
where
F: Fn(),
{
fn new_millis(threshold: time::Duration, f: F) -> Self {
Self { threshold, f }
}
}
impl<F> Reporter for PeriodicReporter<F>
where
F: Fn(),
{
fn handle_stats(&mut self, stats: &mut TotalTimeStats) {
stats.periodically(self.threshold, || (self.f)());
}
}
impl TotalTimeStats {
fn periodically(&mut self, period: time::Duration, f: impl FnOnce()) {
if self.total >= period {
self.total -= period;
(f)();
}
}
pub fn total(&self) -> time::Duration {
self.total
}
pub fn total_mut(&mut self) -> &mut time::Duration {
&mut self.total
}
}
impl<Reporter> crate::Profiler for TotalTimeProfiler<Reporter>
where
Reporter: self::Reporter,
{
fn start(&mut self) {
self.start = time::Instant::now();
}
fn end(&mut self) {
self.stats.current = time::Instant::now()
.duration_since(self.start)
.saturating_sub(time::Duration::from_micros(1));
self.stats.total = self.stats.total.saturating_add(self.stats.current);
let Self {
ref mut reporter,
ref mut stats,
start: _,
} = *self;
reporter.handle_stats(stats);
}
}