use std::{
borrow::Cow,
fmt::Display,
sync::{
atomic::{AtomicUsize, Ordering},
Mutex, MutexGuard,
},
time::Instant,
};
pub struct Timer<const W: usize, const A: usize> {
pub total_count: AtomicUsize,
pub total_time: AtomicUsize,
pub recent_averages: Mutex<Vec<usize>>,
pub current_count: AtomicUsize,
pub current_time: AtomicUsize,
pub logs: Mutex<Vec<Log>>,
}
pub struct Log {
pub level: LogLevel,
pub log: Cow<'static, str>,
}
pub enum LogLevel {
Info,
Warn,
Error,
}
impl Display for LogLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LogLevel::Info => f.write_str("INFO"),
LogLevel::Warn => f.write_str("WARN"),
LogLevel::Error => f.write_str("ERROR"),
}
}
}
impl<const W: usize, const A: usize> Timer<W, A> {
pub fn iteration<T, F: FnOnce() -> T>(&self, iter: F) -> T {
let start = Instant::now();
let output = iter();
self.add_time(
start
.elapsed()
.as_nanos()
.try_into()
.expect("nanos shouldn't overflow usize"),
);
output
}
fn add_time(&self, loop_time: usize) {
self.total_time.fetch_add(loop_time, Ordering::AcqRel);
self.current_time.fetch_add(loop_time, Ordering::AcqRel);
self.total_count.fetch_add(1, Ordering::AcqRel);
let current_count = self.current_count.fetch_add(1, Ordering::AcqRel) + 1;
if current_count == W {
self.current_count.store(0, Ordering::Release);
let current_time: usize = self
.current_time
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |_| Some(0))
.unwrap();
let recent_average: usize = current_time / W;
#[allow(unused_labels)]
'mutex_scope: {
let mut recent_averages: MutexGuard<Vec<usize>> =
self.recent_averages.lock().unwrap();
if recent_averages.len() == A {
recent_averages.remove(0);
}
recent_averages.push(recent_average);
}
}
}
pub fn info<L: Into<Cow<'static, str>>>(&self, log: L) {
self.logs.lock().unwrap().push(Log {
level: LogLevel::Info,
log: log.into(),
});
}
pub fn error<L: Into<Cow<'static, str>>>(&self, log: L) {
self.logs.lock().unwrap().push(Log {
level: LogLevel::Error,
log: log.into(),
});
}
pub fn warn<L: Into<Cow<'static, str>>>(&self, log: L) {
self.logs.lock().unwrap().push(Log {
level: LogLevel::Warn,
log: log.into(),
});
}
}
impl<const W: usize, const A: usize> Default for Timer<W, A> {
fn default() -> Self {
Self {
total_count: AtomicUsize::new(0),
total_time: AtomicUsize::new(0),
recent_averages: Mutex::new(vec![0; A]),
current_count: AtomicUsize::new(0),
current_time: AtomicUsize::new(0),
logs: Mutex::new(vec![]),
}
}
}