cbtop/backend_regression/
mod.rs1mod analysis;
17mod detector;
18mod types;
19
20pub use analysis::{BackendSummary, TransferAnalysis};
21pub use types::{
22 Backend, BackendComparison, BackendMeasurement, BackendRecommendation, SizeCliff, WorkloadType,
23};
24
25use std::collections::HashSet;
26
27#[derive(Debug, Clone)]
29pub struct BackendRegressionDetector {
30 measurements: Vec<BackendMeasurement>,
32 threshold_percent: f64,
34 cliff_threshold_percent: f64,
36 available_backends: Vec<Backend>,
38}
39
40impl Default for BackendRegressionDetector {
41 fn default() -> Self {
42 Self {
43 measurements: Vec::new(),
44 threshold_percent: 10.0,
45 cliff_threshold_percent: 10.0,
46 available_backends: vec![Backend::Scalar, Backend::Sse2, Backend::Avx2],
47 }
48 }
49}
50
51impl BackendRegressionDetector {
52 pub fn new() -> Self {
54 Self::default()
55 }
56
57 pub fn with_threshold(mut self, percent: f64) -> Self {
59 self.threshold_percent = percent;
60 self
61 }
62
63 pub fn with_cliff_threshold(mut self, percent: f64) -> Self {
65 self.cliff_threshold_percent = percent;
66 self
67 }
68
69 pub fn with_backends(mut self, backends: Vec<Backend>) -> Self {
71 self.available_backends = backends;
72 self
73 }
74
75 pub fn add_measurement(&mut self, measurement: BackendMeasurement) {
77 self.measurements.push(measurement);
78 }
79
80 pub fn add(
82 &mut self,
83 backend: Backend,
84 workload: WorkloadType,
85 size: usize,
86 latency_us: f64,
87 throughput: f64,
88 efficiency: f64,
89 ) {
90 self.add_measurement(
91 BackendMeasurement::new(backend, workload, size, latency_us, throughput)
92 .with_efficiency(efficiency),
93 );
94 }
95
96 pub fn measurement_count(&self) -> usize {
98 self.measurements.len()
99 }
100
101 pub(crate) fn measurements(&self) -> &[BackendMeasurement] {
103 &self.measurements
104 }
105
106 pub(crate) fn threshold_percent(&self) -> f64 {
108 self.threshold_percent
109 }
110
111 pub(crate) fn cliff_threshold_percent(&self) -> f64 {
113 self.cliff_threshold_percent
114 }
115
116 pub(crate) fn unique<T: Eq + std::hash::Hash + Copy>(
118 &self,
119 f: impl Fn(&BackendMeasurement) -> T,
120 ) -> Vec<T> {
121 self.measurements
122 .iter()
123 .map(f)
124 .collect::<HashSet<_>>()
125 .into_iter()
126 .collect()
127 }
128
129 pub(crate) fn unique_for<T: Eq + std::hash::Hash + Copy>(
131 &self,
132 workload: WorkloadType,
133 f: impl Fn(&BackendMeasurement) -> T,
134 ) -> Vec<T> {
135 self.measurements
136 .iter()
137 .filter(|m| m.workload == workload)
138 .map(f)
139 .collect::<HashSet<_>>()
140 .into_iter()
141 .collect()
142 }
143
144 pub(crate) fn find_measurement(
146 &self,
147 backend: Backend,
148 workload: WorkloadType,
149 size: usize,
150 ) -> Option<&BackendMeasurement> {
151 self.measurements
152 .iter()
153 .find(|m| m.backend == backend && m.workload == workload && m.size == size)
154 }
155
156 pub fn is_backend_available(&self, backend: Backend) -> bool {
158 self.available_backends.contains(&backend)
159 }
160
161 pub fn available_backends(&self) -> &[Backend] {
163 &self.available_backends
164 }
165
166 pub fn clear(&mut self) {
168 self.measurements.clear();
169 }
170}
171
172#[cfg(test)]
173mod tests;