mod analysis;
mod detector;
mod types;
pub use analysis::{BackendSummary, TransferAnalysis};
pub use types::{
Backend, BackendComparison, BackendMeasurement, BackendRecommendation, SizeCliff, WorkloadType,
};
use std::collections::HashSet;
#[derive(Debug, Clone)]
pub struct BackendRegressionDetector {
measurements: Vec<BackendMeasurement>,
threshold_percent: f64,
cliff_threshold_percent: f64,
available_backends: Vec<Backend>,
}
impl Default for BackendRegressionDetector {
fn default() -> Self {
Self {
measurements: Vec::new(),
threshold_percent: 10.0,
cliff_threshold_percent: 10.0,
available_backends: vec![Backend::Scalar, Backend::Sse2, Backend::Avx2],
}
}
}
impl BackendRegressionDetector {
pub fn new() -> Self {
Self::default()
}
pub fn with_threshold(mut self, percent: f64) -> Self {
self.threshold_percent = percent;
self
}
pub fn with_cliff_threshold(mut self, percent: f64) -> Self {
self.cliff_threshold_percent = percent;
self
}
pub fn with_backends(mut self, backends: Vec<Backend>) -> Self {
self.available_backends = backends;
self
}
pub fn add_measurement(&mut self, measurement: BackendMeasurement) {
self.measurements.push(measurement);
}
pub fn add(
&mut self,
backend: Backend,
workload: WorkloadType,
size: usize,
latency_us: f64,
throughput: f64,
efficiency: f64,
) {
self.add_measurement(
BackendMeasurement::new(backend, workload, size, latency_us, throughput)
.with_efficiency(efficiency),
);
}
pub fn measurement_count(&self) -> usize {
self.measurements.len()
}
pub(crate) fn measurements(&self) -> &[BackendMeasurement] {
&self.measurements
}
pub(crate) fn threshold_percent(&self) -> f64 {
self.threshold_percent
}
pub(crate) fn cliff_threshold_percent(&self) -> f64 {
self.cliff_threshold_percent
}
pub(crate) fn unique<T: Eq + std::hash::Hash + Copy>(
&self,
f: impl Fn(&BackendMeasurement) -> T,
) -> Vec<T> {
self.measurements
.iter()
.map(f)
.collect::<HashSet<_>>()
.into_iter()
.collect()
}
pub(crate) fn unique_for<T: Eq + std::hash::Hash + Copy>(
&self,
workload: WorkloadType,
f: impl Fn(&BackendMeasurement) -> T,
) -> Vec<T> {
self.measurements
.iter()
.filter(|m| m.workload == workload)
.map(f)
.collect::<HashSet<_>>()
.into_iter()
.collect()
}
pub(crate) fn find_measurement(
&self,
backend: Backend,
workload: WorkloadType,
size: usize,
) -> Option<&BackendMeasurement> {
self.measurements
.iter()
.find(|m| m.backend == backend && m.workload == workload && m.size == size)
}
pub fn is_backend_available(&self, backend: Backend) -> bool {
self.available_backends.contains(&backend)
}
pub fn available_backends(&self) -> &[Backend] {
&self.available_backends
}
pub fn clear(&mut self) {
self.measurements.clear();
}
}
#[cfg(test)]
mod tests;