batuta/recipes/
benchmark.rs1use crate::experiment::{CostPerformanceBenchmark, CostPerformancePoint, ExperimentRun};
4use crate::recipes::RecipeResult;
5use std::collections::HashMap;
6
7#[derive(Debug)]
9pub struct CostPerformanceBenchmarkRecipe {
10 benchmark: CostPerformanceBenchmark,
11 pub(crate) budget_constraint: Option<f64>,
12 performance_target: Option<f64>,
13}
14
15impl CostPerformanceBenchmarkRecipe {
16 pub fn new(name: impl Into<String>) -> Self {
18 Self {
19 benchmark: CostPerformanceBenchmark::new(name),
20 budget_constraint: None,
21 performance_target: None,
22 }
23 }
24
25 pub fn with_budget(mut self, max_cost: f64) -> Self {
27 self.budget_constraint = Some(max_cost);
28 self
29 }
30
31 pub fn with_performance_target(mut self, target: f64) -> Self {
33 self.performance_target = Some(target);
34 self
35 }
36
37 pub fn add_run(&mut self, run: &ExperimentRun, performance_metric: &str) {
39 let performance = run.metrics.get(performance_metric).copied().unwrap_or(0.0);
40 let cost = run.cost.as_ref().map(|c| c.total_cost_usd).unwrap_or(0.0);
41 let energy = run.energy.as_ref().map(|e| e.total_joules).unwrap_or(0.0);
42
43 let mut metadata = HashMap::new();
44 metadata.insert("paradigm".to_string(), format!("{:?}", run.paradigm));
45 metadata.insert("device".to_string(), format!("{:?}", run.device));
46 metadata.insert("platform".to_string(), format!("{:?}", run.platform));
47
48 self.benchmark.add_point(CostPerformancePoint {
49 id: run.run_id.clone(),
50 performance,
51 cost,
52 energy_joules: energy,
53 latency_ms: None,
54 metadata,
55 });
56 }
57
58 pub fn analyze(&mut self) -> RecipeResult {
60 contract_pre_analyze!(self);
61 let mut result = RecipeResult::success("cost-performance-benchmark");
62
63 let frontier = self.benchmark.compute_pareto_frontier().to_vec();
65 result = result.with_metric("pareto_optimal_count", frontier.len() as f64);
66 result = result.with_metric("total_configurations", self.benchmark.points.len() as f64);
67
68 if let Some(budget) = self.budget_constraint {
70 if let Some(best) = self.benchmark.best_within_budget(budget) {
71 result = result.with_metric("best_in_budget_performance", best.performance);
72 result = result.with_metric("best_in_budget_cost", best.cost);
73 }
74 }
75
76 if let Some(target) = self.performance_target {
78 let meets_target = self.benchmark.points.iter().any(|p| p.performance >= target);
79 result = result.with_metric("meets_target", if meets_target { 1.0 } else { 0.0 });
80
81 let cheapest =
83 self.benchmark.points.iter().filter(|p| p.performance >= target).min_by(|a, b| {
84 a.cost.partial_cmp(&b.cost).unwrap_or(std::cmp::Ordering::Equal)
85 });
86
87 if let Some(cheapest) = cheapest {
88 result = result.with_metric("cheapest_meeting_target_cost", cheapest.cost);
89 }
90 }
91
92 let efficiency = self.benchmark.efficiency_scores();
94 if !efficiency.is_empty() {
95 let max_efficiency = efficiency.iter().map(|e| e.1).fold(f64::NEG_INFINITY, f64::max);
96 result = result.with_metric("max_efficiency", max_efficiency);
97 }
98
99 result
100 }
101
102 pub fn benchmark(&self) -> &CostPerformanceBenchmark {
104 &self.benchmark
105 }
106
107 pub fn benchmark_mut(&mut self) -> &mut CostPerformanceBenchmark {
109 &mut self.benchmark
110 }
111}