Skip to content

Commit 8673a9c

Browse files
feat: compare against base run and improve results display
- Add --base <run_id> flag to compare the uploaded run against a previous one - Use paginatedCompareRuns when --base is given; warn and fall back to single-run display if the base run is not found - Single-run display now shows a --base hint so users can compare future runs - Comparison display shows the comparison URL - Add build_comparison_table to benchmark_display for side-by-side results Generated with AI Agent (Claude Code)
1 parent edd938c commit 8673a9c

3 files changed

Lines changed: 106 additions & 1 deletion

File tree

src/cli/run/helpers/benchmark_display.rs

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::api_client::FetchLocalRunBenchmarkResult;
1+
use crate::api_client::{
2+
CompareRunsBenchmarkResult, FetchLocalRunBenchmarkResult, ResultComparisonCategory,
3+
};
24
use crate::cli::run::helpers;
35
use crate::executor::ExecutorName;
46
use console::style;
@@ -317,6 +319,102 @@ pub fn build_detailed_summary(result: &FetchLocalRunBenchmarkResult) -> String {
317319
}
318320
}
319321

322+
#[derive(Tabled)]
323+
struct ComparisonRow {
324+
#[tabled(rename = "Benchmark")]
325+
name: String,
326+
#[tabled(rename = "Base")]
327+
base_value: String,
328+
#[tabled(rename = "Head")]
329+
head_value: String,
330+
#[tabled(rename = "Change")]
331+
change: String,
332+
#[tabled(rename = "Status")]
333+
status: String,
334+
}
335+
336+
pub fn build_comparison_table(results: &[CompareRunsBenchmarkResult]) -> String {
337+
let mut grouped: HashMap<&ExecutorName, Vec<&CompareRunsBenchmarkResult>> = HashMap::new();
338+
for result in results {
339+
grouped
340+
.entry(&result.benchmark.executor)
341+
.or_default()
342+
.push(result);
343+
}
344+
345+
let executor_order = [
346+
ExecutorName::Valgrind,
347+
ExecutorName::WallTime,
348+
ExecutorName::Memory,
349+
];
350+
351+
let mut output = String::new();
352+
for executor in &executor_order {
353+
if let Some(executor_results) = grouped.get(executor) {
354+
if !output.is_empty() {
355+
output.push('\n');
356+
}
357+
let rows: Vec<ComparisonRow> = executor_results
358+
.iter()
359+
.map(|result| {
360+
let format_value = |v: Option<f64>| match v {
361+
Some(v) => match executor {
362+
ExecutorName::Memory => helpers::format_memory(v, Some(1)),
363+
_ => helpers::format_duration(v, Some(2)),
364+
},
365+
None => "-".to_string(),
366+
};
367+
368+
let change_str = match result.change {
369+
Some(c) if c > 0.0 => {
370+
let pct = (c * 100.0).round();
371+
format!("{}", style(format!("+{pct}%")).red().bold())
372+
}
373+
Some(c) if c < 0.0 => {
374+
let pct = (c * 100.0).round();
375+
format!("{}", style(format!("{pct}%")).green().bold())
376+
}
377+
Some(_) => format!("{}", style("0%").dim()),
378+
None => "-".to_string(),
379+
};
380+
381+
let status_str = match &result.category {
382+
ResultComparisonCategory::New => {
383+
format!("{}", style("New").cyan().bold())
384+
}
385+
ResultComparisonCategory::Improvement => {
386+
format!("{}", style("Improvement").green().bold())
387+
}
388+
ResultComparisonCategory::Regression => {
389+
format!("{}", style("Regression").red().bold())
390+
}
391+
ResultComparisonCategory::Untouched => {
392+
format!("{}", style("No Change").dim())
393+
}
394+
_ => format!("{}", &result.status),
395+
};
396+
397+
ComparisonRow {
398+
name: result.benchmark.name.clone(),
399+
base_value: format_value(result.base_value),
400+
head_value: format!("{}", style(format_value(result.value)).cyan()),
401+
change: change_str,
402+
status: status_str,
403+
}
404+
})
405+
.collect();
406+
407+
output.push_str(&build_table_with_style(
408+
&rows,
409+
executor.label(),
410+
executor.icon(),
411+
));
412+
}
413+
}
414+
415+
output
416+
}
417+
320418
#[cfg(test)]
321419
mod tests {
322420
use super::*;

src/cli/shared.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ pub struct ExecAndRunSharedArgs {
110110
#[arg(long, default_value = "false")]
111111
pub show_full_output: bool,
112112

113+
/// Compare the results against this base run ID
114+
#[arg(long)]
115+
pub base: Option<String>,
116+
113117
#[command(flatten)]
114118
pub perf_run_args: PerfRunArgs,
115119
}

src/project_config/merger.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ mod tests {
161161
allow_empty: false,
162162
go_runner_version: None,
163163
show_full_output: false,
164+
base: None,
164165
perf_run_args: PerfRunArgs {
165166
enable_perf: true,
166167
perf_unwinding_mode: None,
@@ -195,6 +196,7 @@ mod tests {
195196
allow_empty: false,
196197
go_runner_version: None,
197198
show_full_output: false,
199+
base: None,
198200
perf_run_args: PerfRunArgs {
199201
enable_perf: true,
200202
perf_unwinding_mode: None,
@@ -231,6 +233,7 @@ mod tests {
231233
allow_empty: false,
232234
go_runner_version: None,
233235
show_full_output: false,
236+
base: None,
234237
perf_run_args: PerfRunArgs {
235238
enable_perf: false,
236239
perf_unwinding_mode: None,

0 commit comments

Comments
 (0)