Skip to content

Commit 10a6472

Browse files
committed
create bench_cmp command
1 parent 533f2dd commit 10a6472

File tree

3 files changed

+150
-1
lines changed

3 files changed

+150
-1
lines changed

collector/src/bin/collector.rs

+20-1
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 result.
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

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

collector/src/lib.rs

+1
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)