Skip to content

Commit 671a368

Browse files
committed
create bench_local_diff command
1 parent 533f2dd commit 671a368

File tree

3 files changed

+134
-3
lines changed

3 files changed

+134
-3
lines changed

Cargo.lock

Lines changed: 11 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

collector/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ regex = "1.7.1"
4444
analyzeme = "12.0.0"
4545

4646
benchlib = { path = "benchlib" }
47+
itertools = "0.13.0"
4748

4849
[target.'cfg(windows)'.dependencies]
4950
miow = "0.3"

collector/src/bin/collector.rs

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ use anyhow::Context;
1919
use clap::builder::TypedValueParser;
2020
use clap::{Arg, Parser};
2121
use humansize::{format_size, BINARY};
22+
use itertools::Itertools;
2223
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
2324
use tabled::builder::Builder;
2425
use tabled::settings::object::{Columns, Rows};
2526
use tabled::settings::{Alignment, Border, Color, Modify};
27+
use tabled::{Table, Tabled};
2628
use tokio::runtime::Runtime;
2729

2830
use collector::api::next_artifact::NextArtifact;
@@ -52,7 +54,7 @@ use collector::toolchain::{
5254
use collector::utils::cachegrind::cachegrind_diff;
5355
use collector::utils::{is_installed, wait_for_future};
5456
use collector::{utils, CollectorCtx, CollectorStepBuilder};
55-
use database::{ArtifactId, ArtifactIdNumber, Commit, CommitType, Connection, Pool};
57+
use database::{ArtifactId, ArtifactIdNumber, Commit, CommitType, Connection, Lookup, Pool};
5658

5759
fn n_normal_benchmarks_remaining(n: usize) -> String {
5860
let suffix = if n == 1 { "" } else { "s" };
@@ -628,6 +630,18 @@ enum Commands {
628630
#[command(flatten)]
629631
db: DbOption,
630632
},
633+
634+
/// Displays diff between two local bench result.
635+
BenchLocalDiff {
636+
#[command(flatten)]
637+
db: DbOption,
638+
639+
#[arg(long)]
640+
a_id: String,
641+
642+
#[arg(long)]
643+
b_id: String,
644+
},
631645
}
632646

633647
#[derive(Debug, clap::Parser)]
@@ -1187,6 +1201,113 @@ Make sure to modify `{dir}/perf-config.json` if the category/artifact don't matc
11871201
println!("Data of artifact {name} were removed");
11881202
Ok(0)
11891203
}
1204+
Commands::BenchLocalDiff { db, a_id, b_id } => {
1205+
let pool = Pool::open(&db.db);
1206+
let rt = build_async_runtime();
1207+
let mut conn = rt.block_on(pool.connection());
1208+
let index = rt.block_on(database::Index::load(&mut *conn));
1209+
1210+
let sids = index
1211+
.compile_statistic_descriptions()
1212+
.map(|(_, sid)| sid)
1213+
.collect::<Vec<_>>();
1214+
1215+
let a_id_number = rt
1216+
.block_on(conn.artifact_by_name(&a_id))
1217+
.expect("Cannot find specified artifact")
1218+
.lookup(&index)
1219+
.unwrap();
1220+
let b_id_number = rt
1221+
.block_on(conn.artifact_by_name(&b_id))
1222+
.expect("Cannot find specified artifact")
1223+
.lookup(&index)
1224+
.unwrap();
1225+
1226+
let pstats =
1227+
rt.block_on(conn.get_pstats(&sids, &[Some(a_id_number), Some(b_id_number)]));
1228+
let tuple_pstats = pstats
1229+
.into_iter()
1230+
.map(|row| row.into_iter().collect_tuple::<(_, _)>().unwrap())
1231+
.collect::<Vec<(Option<f64>, Option<f64>)>>();
1232+
1233+
#[derive(Tabled)]
1234+
struct Regression {
1235+
count: usize,
1236+
#[tabled(display_with = "display_range")]
1237+
range: (f64, f64),
1238+
#[tabled(display_with = "display_mean")]
1239+
mean: f64,
1240+
}
1241+
1242+
fn display_range(value: &(f64, f64)) -> String {
1243+
format!("[{:+.2}%, {:+.2}%]", value.0, value.1)
1244+
}
1245+
1246+
fn display_mean(value: &f64) -> String {
1247+
format!("{:+.2}%", value)
1248+
}
1249+
1250+
impl From<&Vec<f64>> for Regression {
1251+
fn from(value: &Vec<f64>) -> Self {
1252+
let min = *value.iter().min_by(|a, b| a.total_cmp(b)).unwrap();
1253+
let max = *value.iter().max_by(|a, b| a.total_cmp(b)).unwrap();
1254+
let count = value.len();
1255+
1256+
Regression {
1257+
range: (min, max),
1258+
count,
1259+
mean: (value.iter().sum::<f64>() / count as f64),
1260+
}
1261+
}
1262+
}
1263+
1264+
const DEFAULT_SIGNIFICANCE_THRESHOLD: f64 = 0.002;
1265+
let change = tuple_pstats
1266+
.iter()
1267+
.filter_map(|&(a, b)| match (a, b) {
1268+
(Some(a), Some(b)) => {
1269+
if a == 0.0 {
1270+
None
1271+
} else {
1272+
Some((b - a) / a)
1273+
}
1274+
}
1275+
(_, _) => None,
1276+
})
1277+
.filter(|c| c.abs() >= DEFAULT_SIGNIFICANCE_THRESHOLD * 100.0)
1278+
.collect::<Vec<_>>();
1279+
let negative_change = change
1280+
.iter()
1281+
.copied()
1282+
.filter(|&c| c < 0.0)
1283+
.collect::<Vec<_>>();
1284+
let positive_change = change
1285+
.iter()
1286+
.copied()
1287+
.filter(|&c| c > 0.0)
1288+
.collect::<Vec<_>>();
1289+
1290+
#[derive(Tabled)]
1291+
struct NamedRegression {
1292+
name: String,
1293+
#[tabled(inline)]
1294+
regression: Regression,
1295+
}
1296+
1297+
let regressions = [negative_change, positive_change, change]
1298+
.into_iter()
1299+
.map(|c| Regression::from(&c))
1300+
.zip(["❌", "✅", "✅, ❌"])
1301+
.map(|(c, label)| NamedRegression {
1302+
name: label.to_string(),
1303+
regression: c,
1304+
})
1305+
.collect::<Vec<_>>();
1306+
1307+
println!("{}", Table::new(regressions));
1308+
1309+
Ok(0)
1310+
}
11901311
}
11911312
}
11921313

@@ -1736,7 +1857,6 @@ fn bench_compile(
17361857
category,
17371858
));
17381859
print_intro();
1739-
17401860
let mut processor = BenchProcessor::new(
17411861
tx.conn(),
17421862
benchmark_name,

0 commit comments

Comments
 (0)