use core::time::Duration;
use super::Sample;
#[derive(Debug, Clone, Copy)]
pub struct CrestModel {
pub peak: usize,
pub floor: Duration,
pub ripple: f64,
pub cov: f64,
}
impl CrestModel {
#[must_use]
pub fn new(peak: usize) -> Self {
Self {
peak,
floor: Duration::from_millis(1),
ripple: 0.01,
cov: 0.02,
}
}
#[must_use]
pub fn rtt(&self, concurrency: usize) -> Duration {
let peak = self.peak.max(1) as f64;
let c = concurrency.max(1) as f64;
if c <= peak {
self.floor
} else {
let stretch = (c / peak) * (c / peak);
self.floor.mul_f64(stretch)
}
}
#[must_use]
pub fn throughput(&self, concurrency: usize) -> f64 {
let rtt_secs = self.rtt(concurrency).as_secs_f64();
let base = concurrency as f64 / rtt_secs;
let phase = ((concurrency.wrapping_mul(2_654_435_761) >> 8) & 0xff) as f64 / 255.0;
let wobble = (phase - 0.5) * 2.0 * self.ripple;
base * (1.0 + wobble)
}
#[must_use]
pub fn sample(&self, concurrency: usize) -> Sample {
let rtt = self.rtt(concurrency);
Sample {
concurrency,
throughput: self.throughput(concurrency),
cov: self.cov,
rtt_min: self.floor,
rtt_p50: rtt,
rtt_p99: rtt,
util: (concurrency as f64 / self.peak.max(1) as f64).min(1.0),
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::float_cmp)]
mod tests {
use super::*;
#[test]
fn throughput_crests_at_peak() {
let model = CrestModel::new(32);
let at_peak = model.throughput(32);
assert!(
model.throughput(64) < at_peak,
"throughput must fall past the crest"
);
assert!(
model.throughput(128) < model.throughput(64),
"monotone decline past crest"
);
assert!(
model.throughput(16) < at_peak,
"throughput rises to the crest"
);
}
#[test]
fn rtt_is_floor_below_crest_and_grows_above() {
let model = CrestModel::new(32);
assert_eq!(model.rtt(16), model.floor);
assert_eq!(model.rtt(32), model.floor);
assert!(model.rtt(64) > model.floor);
}
}