use super::metrics::BenchmarkMetric;
use super::types::PipelineStatus;
use super::metrics::BenchmarkResults;
#[derive(Debug, Clone)]
pub struct MetricRegression {
pub name: String,
pub baseline_mean: f64,
pub current_mean: f64,
pub percent_change: f64,
pub is_regression: bool,
pub is_warning: bool,
pub unit: String,
}
impl MetricRegression {
pub fn from_metrics(
baseline: &BenchmarkMetric,
current: &BenchmarkMetric,
regression_threshold: f64,
warning_threshold: f64,
) -> Self {
let baseline_mean = baseline.mean();
let current_mean = current.mean();
let percent_change = if baseline_mean.abs() > 1e-10 {
((current_mean - baseline_mean) / baseline_mean) * 100.0
} else {
0.0
};
let is_latency_metric = baseline.name.contains("latency")
|| baseline.name.contains("time")
|| baseline.name.contains("duration");
let regression_change = if is_latency_metric {
percent_change } else {
-percent_change };
Self {
name: baseline.name.clone(),
baseline_mean,
current_mean,
percent_change,
is_regression: regression_change >= regression_threshold,
is_warning: regression_change >= warning_threshold
&& regression_change < regression_threshold,
unit: baseline.unit.clone(),
}
}
}
#[derive(Debug, Clone)]
pub struct RegressionAnalysis {
pub baseline: BenchmarkResults,
pub current: BenchmarkResults,
pub regressions: Vec<MetricRegression>,
pub status: PipelineStatus,
pub analysis_duration_ms: u64,
pub summary: String,
}
impl RegressionAnalysis {
pub fn regression_count(&self) -> usize {
self.regressions.iter().filter(|r| r.is_regression).count()
}
pub fn warning_count(&self) -> usize {
self.regressions.iter().filter(|r| r.is_warning).count()
}
pub fn improvement_count(&self) -> usize {
self.regressions
.iter()
.filter(|r| !r.is_regression && !r.is_warning && r.percent_change.abs() > 1.0)
.filter(|r| {
let is_latency = r.name.contains("latency") || r.name.contains("time");
if is_latency {
r.percent_change < 0.0 } else {
r.percent_change > 0.0 }
})
.count()
}
pub fn worst_regression(&self) -> Option<&MetricRegression> {
self.regressions
.iter()
.filter(|r| r.is_regression)
.max_by(|a, b| {
a.percent_change
.abs()
.partial_cmp(&b.percent_change.abs())
.unwrap_or(std::cmp::Ordering::Equal)
})
}
}