use crate::output::analysis::random::Rng;
use crate::output::wrap_yellow;
use std::time::Duration;
pub struct BenchmarkConfig {
pub measurement_time: Duration,
pub num_resamples: usize,
pub num_samples: usize,
pub warm_up_time: Duration,
pub dump_results_to_disk: bool,
pub max_iterations: Option<u64>,
}
impl Default for BenchmarkConfig {
fn default() -> Self {
BenchmarkConfig {
measurement_time: Duration::from_secs(5),
num_resamples: 100_000,
num_samples: 100,
warm_up_time: Duration::from_secs(3),
dump_results_to_disk: true,
max_iterations: None,
}
}
}
pub(crate) fn calculate_iterations(
warmup_mean_execution_time: f64,
num_samples: u64,
target_time: Duration,
) -> Vec<u64> {
let met = warmup_mean_execution_time;
let m_ns = target_time.as_nanos();
let total_runs = num_samples * (num_samples + 1) / 2;
let d = ((m_ns as f64 / met / total_runs as f64).ceil() as u64).max(1);
let expected_nanoseconds = total_runs as f64 * d as f64 * met;
if d == 1 {
let actual_time = Duration::from_nanos(expected_nanoseconds as u64);
println!(
"{} You may wish to increase target time to {:.1?} or lower the requested number of samples",
wrap_yellow(&format!(
"Unable to complete {num_samples} samples in {target_time:.1?}"
)),
actual_time
);
}
(1..=num_samples).map(|a| a * d).collect()
}
pub(crate) fn calculate_t_value(sample_a: &[f64], sample_b: &[f64]) -> f64 {
let a_mean = calculate_mean(sample_a);
let b_mean = calculate_mean(sample_b);
let a_var = calculate_variance(sample_a, a_mean);
let b_var = calculate_variance(sample_b, b_mean);
let a_len = sample_a.len() as f64;
let b_len = sample_b.len() as f64;
let mean_diff = a_mean - b_mean;
let d = (a_var / a_len + b_var / b_len).sqrt();
mean_diff / d
}
pub(crate) fn calculate_mean(a: &[f64]) -> f64 {
a.iter().sum::<f64>() / a.len() as f64
}
pub(crate) fn calculate_variance(sample: &[f64], mean: f64) -> f64 {
let sum = sample
.iter()
.copied()
.map(|val| (val - mean).powi(2))
.sum::<f64>();
sum / (sample.len() as f64 - 1f64) }
pub(crate) fn resample(sample_a: &[f64], sample_b: &[f64], times: usize) -> Vec<f64> {
let a_len = sample_a.len();
let mut combined = Vec::with_capacity(a_len + sample_b.len());
combined.extend_from_slice(sample_a);
combined.extend_from_slice(sample_b);
let mut rng = Rng::new();
let combined_len = combined.len();
let mut distributions = Vec::new();
for _ in 0..times {
let mut sample = Vec::with_capacity(combined_len);
for _ in 0..combined_len {
let index = (rng.next() % combined.len() as u64) as usize;
sample.push(combined[index]);
}
let sample_a = Vec::from(&sample[..a_len]);
let sample_b = Vec::from(&sample[a_len..]);
let t = calculate_t_value(&sample_a, &sample_b);
distributions.push(t);
}
distributions
}
pub(crate) fn calculate_p_value(total_t: f64, distribution: &[f64]) -> f64 {
let hits = distribution.iter().filter(|x| x < &&total_t).count();
let tails = 2; let min = std::cmp::min(hits, distribution.len() - hits);
(min * tails) as f64 / distribution.len() as f64
}
#[inline]
pub(crate) fn calculate_median(sample: &mut [f64]) -> f64 {
sample.sort_by(f64::total_cmp);
sample.get(sample.len() / 2).copied().unwrap_or_default()
}
pub(crate) struct SamplingDataSimpleAnalysis {
pub(crate) elapsed: u128,
pub(crate) min: f64,
pub(crate) max: f64,
pub(crate) average: f64,
pub(crate) median: f64,
pub(crate) variance: f64,
pub(crate) stddev: f64,
pub(crate) per_sample_average: Vec<f64>,
}
#[cfg(test)]
mod tests {
use crate::output::analysis::criterion::{
calculate_mean, calculate_t_value, calculate_variance,
};
#[test]
fn calculates_mean() {
let data = vec![46.0, 69.0, 32.0, 60.0, 52.0, 41.0];
assert!(calculate_mean(&data) - 50.0 < 0.0000_001);
}
#[test]
fn calculates_variance() {
let data = vec![46.0, 69.0, 32.0, 60.0, 52.0, 41.0];
assert!(calculate_variance(&data, 50.0) - 177.2 < 0.00001);
}
#[test]
fn calculate_t() {
let sample_a = vec![19.7, 20.4, 19.6, 17.8, 18.5, 18.9, 18.3, 18.9, 19.5, 21.95];
let sample_b = vec![
28.3, 26.7, 20.1, 23.3, 25.2, 22.1, 17.7, 27.6, 20.6, 13.7, 23.2, 17.5, 20.6, 18.0,
23.9, 21.6, 24.3, 20.4, 23.9, 13.3,
];
assert!(calculate_t_value(&sample_a, &sample_b).abs() - 2.24787 < 0.0001);
}
}