use crate::measurement::{ BenchmarkResult, Comparison };
use std::collections::HashMap;
pub struct ComparativeAnalysis {
name: String,
variants: HashMap<String, Box<dyn FnMut() + Send>>,
}
impl std::fmt::Debug for ComparativeAnalysis {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ComparativeAnalysis")
.field("name", &self.name)
.field("variants", &format!("{} variants", self.variants.len()))
.finish()
}
}
impl ComparativeAnalysis {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
variants: HashMap::new(),
}
}
#[must_use]
pub fn add_variant<F>(mut self, name: impl Into<String>, f: F) -> Self
where
F: FnMut() + Send + 'static,
{
self.variants.insert(name.into(), Box::new(f));
self
}
#[must_use]
pub fn algorithm<F>(self, name: impl Into<String>, f: F) -> Self
where
F: FnMut() + Send + 'static,
{
self.add_variant(name, f)
}
#[must_use]
pub fn run(self) -> ComparisonAnalysisReport {
let mut results = HashMap::new();
for (name, variant) in self.variants {
let result = crate::measurement::bench_function(&name, variant);
results.insert(name.clone(), result);
}
ComparisonAnalysisReport {
name: self.name,
results,
}
}
}
#[derive(Debug)]
pub struct ComparisonAnalysisReport {
pub name: String,
pub results: HashMap<String, BenchmarkResult>,
}
impl ComparisonAnalysisReport {
#[must_use]
pub fn fastest(&self) -> Option<(&String, &BenchmarkResult)> {
self.results
.iter()
.min_by(|a, b| a.1.mean_time().cmp(&b.1.mean_time()))
}
#[must_use]
pub fn slowest(&self) -> Option<(&String, &BenchmarkResult)> {
self.results
.iter()
.max_by(|a, b| a.1.mean_time().cmp(&b.1.mean_time()))
}
#[must_use]
pub fn sorted_by_performance(&self) -> Vec<(&String, &BenchmarkResult)> {
let mut results: Vec<_> = self.results.iter().collect();
results.sort_by(|a, b| a.1.mean_time().cmp(&b.1.mean_time()));
results
}
pub fn print_summary(&self) {
println!("=== {} Comparison ===", self.name);
if let Some((fastest_name, fastest_result)) = self.fastest() {
println!("🏆 Fastest: {} ({:.2?})", fastest_name, fastest_result.mean_time());
println!("\nRelative Performance:");
for (name, result) in self.sorted_by_performance() {
let _comparison = result.compare(fastest_result);
let relative_speed = if name == fastest_name {
"baseline".to_string()
} else {
format!("{:.1}x slower",
result.mean_time().as_secs_f64() / fastest_result.mean_time().as_secs_f64())
};
println!(" {} - {:.2?} ({})", name, result.mean_time(), relative_speed);
}
}
println!(); }
#[must_use]
pub fn to_markdown(&self) -> String {
let mut output = String::new();
output.push_str(&format!("## {} Comparison\n\n", self.name));
if self.results.is_empty() {
output.push_str("No results available.\n");
return output;
}
output.push_str("| Algorithm | Mean Time | Operations/sec | Relative Performance |\n");
output.push_str("|-----------|-----------|----------------|----------------------|\n");
let fastest = self.fastest().map(|(_, result)| result);
for (name, result) in self.sorted_by_performance() {
let relative = if let Some(fastest_result) = fastest {
if result.mean_time() == fastest_result.mean_time() {
"**Fastest**".to_string()
} else {
format!("{:.1}x slower",
result.mean_time().as_secs_f64() / fastest_result.mean_time().as_secs_f64())
}
} else {
"N/A".to_string()
};
output.push_str(&format!("| {} | {:.2?} | {:.0} | {} |\n",
name,
result.mean_time(),
result.operations_per_second(),
relative));
}
output.push('\n');
if let (Some((fastest_name, _)), Some((slowest_name, slowest_result))) =
(self.fastest(), self.slowest()) {
output.push_str("### Key Insights\n\n");
output.push_str(&format!("- **Best performing**: {fastest_name} algorithm\n"));
if fastest_name != slowest_name {
if let Some((_, fastest)) = self.fastest() {
let speedup = slowest_result.mean_time().as_secs_f64() / fastest.mean_time().as_secs_f64();
output.push_str(&format!("- **Performance range**: {speedup:.1}x difference between fastest and slowest\n"));
}
}
}
output
}
}
#[derive(Debug, Clone)]
pub struct RegressionAnalysis {
pub baseline_results: HashMap<String, BenchmarkResult>,
pub current_results: HashMap<String, BenchmarkResult>,
}
impl RegressionAnalysis {
#[must_use]
pub fn new(
baseline: HashMap<String, BenchmarkResult>,
current: HashMap<String, BenchmarkResult>
) -> Self {
Self {
baseline_results: baseline,
current_results: current,
}
}
#[must_use]
pub fn detect_regressions(&self, threshold_percent: f64) -> Vec<Comparison> {
let mut regressions = Vec::new();
for (name, current) in &self.current_results {
if let Some(baseline) = self.baseline_results.get(name) {
let comparison = current.compare(baseline);
if comparison.improvement_percentage < -threshold_percent {
regressions.push(comparison);
}
}
}
regressions
}
#[must_use]
pub fn detect_improvements(&self, threshold_percent: f64) -> Vec<Comparison> {
let mut improvements = Vec::new();
for (name, current) in &self.current_results {
if let Some(baseline) = self.baseline_results.get(name) {
let comparison = current.compare(baseline);
if comparison.improvement_percentage > threshold_percent {
improvements.push(comparison);
}
}
}
improvements
}
#[must_use]
pub fn worst_regression_percentage(&self) -> f64 {
self.detect_regressions(0.0)
.iter()
.map(|c| c.improvement_percentage.abs())
.fold(0.0, f64::max)
}
#[must_use]
pub fn generate_report(&self) -> String {
let mut report = String::new();
report.push_str("# Performance Regression Analysis\n\n");
let regressions = self.detect_regressions(5.0);
let improvements = self.detect_improvements(5.0);
if !regressions.is_empty() {
report.push_str("## 🚨 Performance Regressions\n\n");
for regression in ®ressions {
report.push_str(&format!("- **{}**: {:.1}% slower ({:.2?} -> {:.2?})\n",
regression.current.name,
regression.improvement_percentage.abs(),
regression.baseline.mean_time(),
regression.current.mean_time()));
}
report.push('\n');
}
if !improvements.is_empty() {
report.push_str("## 🎉 Performance Improvements\n\n");
for improvement in &improvements {
report.push_str(&format!("- **{}**: {:.1}% faster ({:.2?} -> {:.2?})\n",
improvement.current.name,
improvement.improvement_percentage,
improvement.baseline.mean_time(),
improvement.current.mean_time()));
}
report.push('\n');
}
if regressions.is_empty() && improvements.is_empty() {
report.push_str("## ✅ No Significant Changes\n\n");
report.push_str("Performance appears stable compared to baseline.\n\n");
}
report
}
}