use crate::{simd, stats};
pub trait Dispatch {
fn sum(&self, x: &[f64]) -> f64 {
simd::sum(x)
}
fn mean(&self, x: &[f64]) -> f64 {
stats::mean(x)
}
fn variance(&self, x: &[f64]) -> f64 {
stats::variance(x)
}
}
pub struct Core;
impl Dispatch for Core {}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
use std::time::Instant;
struct Layer {
seen: RefCell<Vec<&'static str>>,
}
impl Dispatch for Layer {
fn sum(&self, _x: &[f64]) -> f64 {
self.seen.borrow_mut().push("sum");
-1.0
}
fn mean(&self, x: &[f64]) -> f64 {
self.seen.borrow_mut().push("mean");
Core.mean(x)
}
}
#[test]
fn layer_handles_falls_through_and_leaves_core_unchanged() {
let x: Vec<f64> = (0..5).map(f64::from).collect();
let layer = Layer { seen: RefCell::new(Vec::new()) };
assert_eq!(Core.sum(&x), 10.0);
assert_eq!(layer.sum(&x), -1.0);
assert_eq!(layer.mean(&x), 2.0);
assert_eq!(layer.variance(&x), stats::variance(&x));
assert_eq!(Core.sum(&x), 10.0);
let core_dyn: &dyn Dispatch = &Core;
let layer_dyn: &dyn Dispatch = &layer;
assert_eq!(core_dyn.sum(&x), 10.0);
assert_eq!(layer_dyn.sum(&x), -1.0);
assert_eq!(*layer.seen.borrow(), ["sum", "mean", "sum"]);
}
fn calls_per_second(mut f: impl FnMut() -> f64) -> f64 {
let reps = 2000u32;
let mut best = f64::MAX;
for _ in 0..21 {
let t = Instant::now();
for _ in 0..reps {
crate::bench::slain(f());
}
best = best.min(t.elapsed().as_secs_f64());
}
f64::from(reps) / best
}
#[test]
#[ignore]
fn time_dispatch() {
let x: Vec<f64> = (0..4096).map(|i| f64::from(i) * 0.5).collect();
let layer = Layer { seen: RefCell::new(Vec::new()) };
let core_dyn: &dyn Dispatch = &Core;
let direct = calls_per_second(|| stats::mean(crate::bench::slain(&x)));
let fixed = calls_per_second(|| Core.mean(crate::bench::slain(&x)));
let dynamic = calls_per_second(|| crate::bench::slain(core_dyn).mean(crate::bench::slain(&x)));
let through = calls_per_second(|| {
layer.seen.borrow_mut().clear();
layer.mean(crate::bench::slain(&x))
});
for (name, c) in [("direct stats::mean", direct), ("static Core.mean", fixed), ("dyn Dispatch mean (Core)", dynamic), ("layer fall-through mean", through)] {
println!("DISPATCH {name:28} {c:.9} calls/s {:.9}x direct", c / direct);
}
}
}