use deno_core::serde::Deserialize;
use deno_core::serde::Serialize;
use std::collections::HashMap;
use std::collections::VecDeque;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Duration;
use std::time::Instant;
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PerformanceAverage {
pub name: String,
pub count: u32,
pub average_duration: u32,
}
#[derive(Debug)]
pub struct PerformanceMark {
name: String,
count: u32,
start: Instant,
}
#[derive(Debug, Clone)]
pub struct PerformanceMeasure {
pub name: String,
pub count: u32,
pub duration: Duration,
}
impl From<PerformanceMark> for PerformanceMeasure {
fn from(value: PerformanceMark) -> Self {
Self {
name: value.name,
count: value.count,
duration: value.start.elapsed(),
}
}
}
#[derive(Debug, Clone)]
pub struct Performance {
counts: Arc<Mutex<HashMap<String, u32>>>,
max_size: usize,
measures: Arc<Mutex<VecDeque<PerformanceMeasure>>>,
}
impl Default for Performance {
fn default() -> Self {
Self {
counts: Default::default(),
max_size: 1_000,
measures: Default::default(),
}
}
}
impl Performance {
#[cfg(test)]
pub fn average(&self, name: &str) -> Option<(usize, Duration)> {
let mut items = Vec::new();
for measure in self.measures.lock().unwrap().iter() {
if measure.name == name {
items.push(measure.duration);
}
}
let len = items.len();
if len > 0 {
let average = items.into_iter().sum::<Duration>() / len as u32;
Some((len, average))
} else {
None
}
}
pub fn averages(&self) -> Vec<PerformanceAverage> {
let mut averages: HashMap<String, Vec<Duration>> = HashMap::new();
for measure in self.measures.lock().unwrap().iter() {
averages
.entry(measure.name.clone())
.or_default()
.push(measure.duration);
}
averages
.into_iter()
.map(|(k, d)| {
let a = d.clone().into_iter().sum::<Duration>() / d.len() as u32;
PerformanceAverage {
name: k,
count: d.len() as u32,
average_duration: a.as_millis() as u32,
}
})
.collect()
}
pub fn mark<S: AsRef<str>>(&self, name: S) -> PerformanceMark {
let name = name.as_ref();
let mut counts = self.counts.lock().unwrap();
let count = counts.entry(name.to_string()).or_insert(0);
*count += 1;
PerformanceMark {
name: name.to_string(),
count: *count,
start: Instant::now(),
}
}
pub fn measure(&self, mark: PerformanceMark) -> Duration {
let measure = PerformanceMeasure::from(mark);
let duration = measure.duration;
let mut measures = self.measures.lock().unwrap();
measures.push_back(measure);
while measures.len() > self.max_size {
measures.pop_front();
}
duration
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_average() {
let performance = Performance::default();
let mark1 = performance.mark("a");
let mark2 = performance.mark("a");
let mark3 = performance.mark("b");
performance.measure(mark2);
performance.measure(mark1);
performance.measure(mark3);
let (count, _) = performance.average("a").expect("should have had value");
assert_eq!(count, 2);
let (count, _) = performance.average("b").expect("should have had value");
assert_eq!(count, 1);
assert!(performance.average("c").is_none());
}
#[test]
fn test_averages() {
let performance = Performance::default();
let mark1 = performance.mark("a");
let mark2 = performance.mark("a");
performance.measure(mark2);
performance.measure(mark1);
let averages = performance.averages();
assert_eq!(averages.len(), 1);
assert_eq!(averages[0].count, 2);
}
}