use std::{collections::HashMap, time::Duration, arch::asm};
pub trait Clock {
fn now() -> Self;
fn set_scaling_factor(&mut self, scaling: usize);
fn duration_since(&self, earlier: Self) -> Duration;
}
#[inline(always)]
pub fn rdtsc() -> u64 {
let hi: u32;
let lo: u32;
unsafe {
asm!("rdtsc", out("eax") lo, out("edx") hi);
}
u64::from(lo) | (u64::from(hi) << 32)
}
pub struct Tsc {
tsc: u64,
freq: Option<usize>,
}
impl Clock for Tsc {
#[inline(always)]
fn now() -> Self {
Tsc {
tsc: rdtsc(),
freq: None,
}
}
fn set_scaling_factor(&mut self, freq: usize) {
self.freq = Some(freq);
}
fn duration_since(&self, earlier: Self) -> Duration {
assert!(earlier.tsc < self.tsc);
let diff = self.tsc - earlier.tsc;
let nanos = diff * 1000 / self.freq.unwrap() as u64;
Duration::from_nanos(nanos)
}
}
impl Clock for std::time::Instant {
#[inline(always)]
fn now() -> Self {
std::time::Instant::now()
}
fn set_scaling_factor(&mut self, _freq: usize) {
}
fn duration_since(&self, earlier: Self) -> Duration {
self.duration_since(earlier)
}
}
#[cfg(test)]
mod test {
use super::Clock;
use std::time::Instant;
#[test]
fn test_instant_clock() {
let earlier = Instant::now();
let _ = <Instant as Clock>::duration_since(&Instant::now(), earlier);
}
}
#[derive(Default, Debug)]
pub struct MemoizedTimingData {
cached_avg: Option<f64>,
cached_sd: Option<f64>,
cached_max: Option<f64>,
cached_sorted: Option<Vec<u64>>,
cached_percentiles: HashMap<usize, f64>,
}
impl MemoizedTimingData {
pub fn new() -> Self {
Self {
cached_avg: None,
cached_sd: None,
cached_max: None,
cached_sorted: None,
cached_percentiles: HashMap::new(),
}
}
pub fn avg(&mut self, measurements: &[u64]) -> f64 {
if let Some(avg) = self.cached_avg {
return avg;
}
let n = measurements.len();
let sum: u64 = measurements.iter().sum();
let avg = (sum as f64) / (n as f64);
self.cached_avg = Some(avg);
avg
}
pub fn sd(&mut self, measurements: &[u64]) -> f64 {
if let Some(sd) = self.cached_sd {
return sd;
}
let n = measurements.len() as f64;
let avg = self.avg(measurements);
let deviations_sq: f64 = measurements.iter().map(|&x| (x as f64 - avg).powi(2)).sum();
let sd = (deviations_sq / n).sqrt();
self.cached_sd = Some(sd);
sd
}
fn sorted_data(&mut self, measurements: &[u64]) -> &Vec<u64> {
if let Some(ref sorted) = self.cached_sorted {
return sorted;
}
let mut clone = measurements.to_vec();
clone.sort_unstable();
self.cached_sorted = Some(clone);
self.cached_sorted.as_ref().unwrap()
}
pub fn percentile(&mut self, measurements: &[u64], percentile: usize) -> f64 {
assert!(percentile < 100);
if let Some(&percentile) = self.cached_percentiles.get(&percentile) {
return percentile;
}
let val = {
let sorted = self.sorted_data(measurements);
let idx = sorted.len() * percentile / 100;
assert!(idx < sorted.len());
sorted[idx] as f64
};
self.cached_percentiles.insert(percentile, val);
val
}
pub fn permicrotile(&mut self, measurements: &[u64], permicrotile: usize) -> f64 {
assert!(permicrotile < 1_000_000 && permicrotile > 990_000);
if let Some(&permicrotile) = self.cached_percentiles.get(&permicrotile) {
return permicrotile;
}
let val = {
let sorted = self.sorted_data(measurements);
let idx = sorted.len() * permicrotile / 1_000_000;
assert!(idx < sorted.len());
sorted[idx] as f64
};
self.cached_percentiles.insert(permicrotile, val);
val
}
pub fn max(&mut self, measurements: &[u64]) -> f64 {
if let Some(max) = self.cached_max {
return max;
}
let val = {
let sorted = self.sorted_data(measurements);
*sorted.last().unwrap() as f64
};
self.cached_max = Some(val);
val
}
}