use deno_core::parking_lot::Mutex;use deno_core::serde::Deserialize;use deno_core::serde::Serialize;use deno_core::serde_json::json;use std::cmp;use std::collections::HashMap;use std::collections::VecDeque;use std::fmt;use std::time::Duration;use std::time::Instant;
use super::logging::lsp_debug;
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]#[serde(rename_all = "camelCase")]pub struct PerformanceAverage { pub name: String, pub count: u32, pub average_duration: u32,}
impl PartialOrd for PerformanceAverage { fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) }}
impl Ord for PerformanceAverage { fn cmp(&self, other: &Self) -> cmp::Ordering { self.name.cmp(&other.name) }}
#[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 fmt::Display for PerformanceMeasure { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} ({}ms)", self.name, self.duration.as_millis()) }}
impl From<PerformanceMark> for PerformanceMeasure { fn from(value: PerformanceMark) -> Self { Self { name: value.name, count: value.count, duration: value.start.elapsed(), } }}
#[derive(Debug)]pub struct Performance { counts: Mutex<HashMap<String, u32>>, max_size: usize, measures: Mutex<VecDeque<PerformanceMeasure>>,}
impl Default for Performance { fn default() -> Self { Self { counts: Default::default(), max_size: 3_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().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().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>, V: Serialize>( &self, name: S, maybe_args: Option<V>, ) -> PerformanceMark { let name = name.as_ref(); let mut counts = self.counts.lock(); let count = counts.entry(name.to_string()).or_insert(0); *count += 1; let msg = if let Some(args) = maybe_args { json!({ "type": "mark", "name": name, "count": count, "args": args, }) } else { json!({ "type": "mark", "name": name, }) }; lsp_debug!("{},", msg); PerformanceMark { name: name.to_string(), count: *count, start: Instant::now(), } }
pub fn measure(&self, mark: PerformanceMark) -> Duration { let measure = PerformanceMeasure::from(mark); lsp_debug!( "{},", json!({ "type": "measure", "name": measure.name, "count": measure.count, "duration": measure.duration.as_millis() as u32, }) ); let duration = measure.duration; let mut measures = self.measures.lock(); measures.push_front(measure); while measures.len() > self.max_size { measures.pop_back(); } duration }
pub fn to_vec(&self) -> Vec<PerformanceMeasure> { let measures = self.measures.lock(); measures.iter().cloned().collect() }}
#[cfg(test)]mod tests { use super::*;
#[test] fn test_average() { let performance = Performance::default(); let mark1 = performance.mark("a", None::<()>); let mark2 = performance.mark("a", None::<()>); let mark3 = performance.mark("b", None::<()>); 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", None::<()>); let mark2 = performance.mark("a", None::<()>); performance.measure(mark2); performance.measure(mark1); let averages = performance.averages(); assert_eq!(averages.len(), 1); assert_eq!(averages[0].count, 2); }}