1use crate::{simd, stats};
2
3pub trait Dispatch {
4 fn sum(&self, x: &[f64]) -> f64 {
5 simd::sum(x)
6 }
7
8 fn mean(&self, x: &[f64]) -> f64 {
9 stats::mean(x)
10 }
11
12 fn variance(&self, x: &[f64]) -> f64 {
13 stats::variance(x)
14 }
15}
16
17pub struct Core;
18
19impl Dispatch for Core {}
20
21#[cfg(test)]
22mod tests {
23 use super::*;
24 use std::cell::RefCell;
25 use std::time::Instant;
26
27 struct Layer {
28 seen: RefCell<Vec<&'static str>>,
29 }
30
31 impl Dispatch for Layer {
32 fn sum(&self, _x: &[f64]) -> f64 {
33 self.seen.borrow_mut().push("sum");
34 -1.0
35 }
36
37 fn mean(&self, x: &[f64]) -> f64 {
38 self.seen.borrow_mut().push("mean");
39 Core.mean(x)
40 }
41 }
42
43 #[test]
44 fn layer_handles_falls_through_and_leaves_core_unchanged() {
45 let x: Vec<f64> = (0..5).map(f64::from).collect();
46 let layer = Layer { seen: RefCell::new(Vec::new()) };
47 assert_eq!(Core.sum(&x), 10.0);
48 assert_eq!(layer.sum(&x), -1.0);
49 assert_eq!(layer.mean(&x), 2.0);
50 assert_eq!(layer.variance(&x), stats::variance(&x));
51 assert_eq!(Core.sum(&x), 10.0);
52 let core_dyn: &dyn Dispatch = &Core;
53 let layer_dyn: &dyn Dispatch = &layer;
54 assert_eq!(core_dyn.sum(&x), 10.0);
55 assert_eq!(layer_dyn.sum(&x), -1.0);
56 assert_eq!(*layer.seen.borrow(), ["sum", "mean", "sum"]);
57 }
58
59 fn calls_per_second(mut f: impl FnMut() -> f64) -> f64 {
60 let reps = 2000u32;
61 let mut best = f64::MAX;
62 for _ in 0..21 {
63 let t = Instant::now();
64 for _ in 0..reps {
65 crate::bench::slain(f());
66 }
67 best = best.min(t.elapsed().as_secs_f64());
68 }
69 f64::from(reps) / best
70 }
71
72 #[test]
73 #[ignore]
74 fn time_dispatch() {
75 let x: Vec<f64> = (0..4096).map(|i| f64::from(i) * 0.5).collect();
76 let layer = Layer { seen: RefCell::new(Vec::new()) };
77 let core_dyn: &dyn Dispatch = &Core;
78 let direct = calls_per_second(|| stats::mean(crate::bench::slain(&x)));
79 let fixed = calls_per_second(|| Core.mean(crate::bench::slain(&x)));
80 let dynamic = calls_per_second(|| crate::bench::slain(core_dyn).mean(crate::bench::slain(&x)));
81 let through = calls_per_second(|| {
82 layer.seen.borrow_mut().clear();
83 layer.mean(crate::bench::slain(&x))
84 });
85 for (name, c) in [("direct stats::mean", direct), ("static Core.mean", fixed), ("dyn Dispatch mean (Core)", dynamic), ("layer fall-through mean", through)] {
86 println!("DISPATCH {name:28} {c:.9} calls/s {:.9}x direct", c / direct);
87 }
88 }
89}