Skip to content

Commit 239b046

Browse files
authored
Merge pull request #1955 from s7tya/add-bench-cmp-command
Create `bench_cmp` command
2 parents 8df1e5d + 9189a3a commit 239b046

File tree

3 files changed

+147
-1
lines changed

3 files changed

+147
-1
lines changed

collector/src/bin/collector.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use std::{str, time::Instant};
1818
use anyhow::Context;
1919
use clap::builder::TypedValueParser;
2020
use clap::{Arg, Parser};
21+
use collector::compare::compare_artifacts;
2122
use humansize::{format_size, BINARY};
2223
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
2324
use tabled::builder::Builder;
@@ -628,6 +629,18 @@ enum Commands {
628629
#[command(flatten)]
629630
db: DbOption,
630631
},
632+
633+
/// Displays diff between two local bench results.
634+
BenchCmp {
635+
#[command(flatten)]
636+
db: DbOption,
637+
638+
/// The name of the base artifact to be compared.
639+
base: String,
640+
641+
/// The name of the modified artifact to be compared.
642+
modified: String,
643+
},
631644
}
632645

633646
#[derive(Debug, clap::Parser)]
@@ -1187,6 +1200,13 @@ Make sure to modify `{dir}/perf-config.json` if the category/artifact don't matc
11871200
println!("Data of artifact {name} were removed");
11881201
Ok(0)
11891202
}
1203+
Commands::BenchCmp { db, base, modified } => {
1204+
let pool = Pool::open(&db.db);
1205+
let rt = build_async_runtime();
1206+
let conn = rt.block_on(pool.connection());
1207+
rt.block_on(compare_artifacts(conn, base, modified))?;
1208+
Ok(0)
1209+
}
11901210
}
11911211
}
11921212

@@ -1736,7 +1756,6 @@ fn bench_compile(
17361756
category,
17371757
));
17381758
print_intro();
1739-
17401759
let mut processor = BenchProcessor::new(
17411760
tx.conn(),
17421761
benchmark_name,

collector/src/compare.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
use std::sync::Arc;
2+
3+
use database::{
4+
metric::Metric,
5+
selector::{BenchmarkQuery, CompileBenchmarkQuery},
6+
ArtifactId, Connection,
7+
};
8+
use tabled::{Table, Tabled};
9+
10+
/// Compare 2 artifacts and print the result.
11+
pub async fn compare_artifacts(
12+
mut conn: Box<dyn Connection>,
13+
base: String,
14+
modified: String,
15+
) -> anyhow::Result<()> {
16+
let index = database::Index::load(&mut *conn).await;
17+
18+
let query = CompileBenchmarkQuery::default()
19+
.metric(database::selector::Selector::One(Metric::InstructionsUser));
20+
let resp = query
21+
.execute(
22+
&mut *conn,
23+
&index,
24+
Arc::new(vec![ArtifactId::Tag(base), ArtifactId::Tag(modified)]),
25+
)
26+
.await
27+
.unwrap();
28+
29+
let tuple_pstats = resp
30+
.into_iter()
31+
.map(|resp| {
32+
let points = resp.series.points.collect::<Vec<_>>();
33+
(points[0], points[1])
34+
})
35+
.collect::<Vec<_>>();
36+
37+
#[derive(Tabled)]
38+
struct Regression {
39+
count: usize,
40+
#[tabled(display_with = "display_range")]
41+
range: (Option<f64>, Option<f64>),
42+
#[tabled(display_with = "display_mean")]
43+
mean: Option<f64>,
44+
}
45+
46+
fn format_value(value: Option<f64>) -> String {
47+
match value {
48+
Some(value) => format!("{:+.2}%", value),
49+
None => "-".to_string(),
50+
}
51+
}
52+
53+
fn display_range(&(min, max): &(Option<f64>, Option<f64>)) -> String {
54+
format!("[{}, {}]", &format_value(min), &format_value(max))
55+
}
56+
57+
fn display_mean(value: &Option<f64>) -> String {
58+
match value {
59+
Some(value) => format!("{:+.2}%", value),
60+
None => "-".to_string(),
61+
}
62+
}
63+
64+
impl From<&Vec<f64>> for Regression {
65+
fn from(value: &Vec<f64>) -> Self {
66+
let min = value.iter().copied().min_by(|a, b| a.total_cmp(b));
67+
let max = value.iter().copied().max_by(|a, b| a.total_cmp(b));
68+
let count = value.len();
69+
70+
Regression {
71+
range: (min, max),
72+
count,
73+
mean: if count == 0 {
74+
None
75+
} else {
76+
Some(value.iter().sum::<f64>() / count as f64)
77+
},
78+
}
79+
}
80+
}
81+
82+
let change = tuple_pstats
83+
.iter()
84+
.filter_map(|&(a, b)| match (a, b) {
85+
(Some(a), Some(b)) => {
86+
if a == 0.0 {
87+
None
88+
} else {
89+
Some((b - a) / a)
90+
}
91+
}
92+
(_, _) => None,
93+
})
94+
.collect::<Vec<_>>();
95+
let negative_change = change
96+
.iter()
97+
.copied()
98+
.filter(|&c| c < 0.0)
99+
.collect::<Vec<_>>();
100+
let positive_change = change
101+
.iter()
102+
.copied()
103+
.filter(|&c| c > 0.0)
104+
.collect::<Vec<_>>();
105+
106+
#[derive(Tabled)]
107+
struct NamedRegression {
108+
name: String,
109+
#[tabled(inline)]
110+
regression: Regression,
111+
}
112+
113+
let regressions = [negative_change, positive_change, change]
114+
.into_iter()
115+
.map(|c| Regression::from(&c))
116+
.zip(["❌", "✅", "✅, ❌"])
117+
.map(|(c, label)| NamedRegression {
118+
name: label.to_string(),
119+
regression: c,
120+
})
121+
.collect::<Vec<_>>();
122+
123+
println!("{}", Table::new(regressions));
124+
125+
Ok(())
126+
}

collector/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub mod api;
99
pub mod artifact_stats;
1010
pub mod cargo;
1111
pub mod codegen;
12+
pub mod compare;
1213
pub mod compile;
1314
pub mod runtime;
1415
pub mod toolchain;

0 commit comments

Comments
 (0)