Skip to content

Commit 7e3784d

Browse files
committed
Get comparison in order
1 parent 39608fb commit 7e3784d

File tree

2 files changed

+111
-80
lines changed

2 files changed

+111
-80
lines changed

site/src/api.rs

+16-8
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ pub mod bootstrap {
138138
}
139139

140140
pub mod comparison {
141-
use crate::comparison;
142141
use collector::Bound;
143142
use database::Date;
144143
use serde::{Deserialize, Serialize};
@@ -153,13 +152,12 @@ pub mod comparison {
153152

154153
#[derive(Debug, Clone, Serialize)]
155154
pub struct Response {
156-
/// The variance data for each benchmark, if any.
157-
pub variance: Option<HashMap<String, comparison::BenchmarkVariance>>,
158155
/// The names for the previous artifact before `a`, if any.
159156
pub prev: Option<String>,
160157

161-
pub a: ArtifactData,
162-
pub b: ArtifactData,
158+
pub a: ArtifactDescription,
159+
pub b: ArtifactDescription,
160+
pub comparisons: Vec<Comparison>,
163161

164162
/// The names for the next artifact after `b`, if any.
165163
pub next: Option<String>,
@@ -169,15 +167,25 @@ pub mod comparison {
169167
pub is_contiguous: bool,
170168
}
171169

172-
/// A serializable wrapper for `comparison::ArtifactData`.
173170
#[derive(Debug, Clone, Serialize)]
174-
pub struct ArtifactData {
171+
pub struct ArtifactDescription {
175172
pub commit: String,
176173
pub date: Option<Date>,
177174
pub pr: Option<u32>,
178-
pub data: HashMap<String, Vec<(String, f64)>>,
179175
pub bootstrap: HashMap<String, u64>,
180176
}
177+
178+
/// A serializable wrapper for `comparison::ArtifactData`.
179+
#[derive(Debug, Clone, Serialize)]
180+
pub struct Comparison {
181+
pub benchmark: String,
182+
pub profile: String,
183+
pub scenario: String,
184+
pub is_significant: bool,
185+
pub is_dodgy: bool,
186+
pub historical_statistics: Option<Vec<f64>>,
187+
pub statistics: (f64, f64),
188+
}
181189
}
182190

183191
pub mod status {

site/src/comparison.rs

+95-72
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use collector::Bound;
1212
use log::debug;
1313
use serde::Serialize;
1414

15-
use std::collections::HashMap;
15+
use std::collections::{HashMap, HashSet};
1616
use std::error::Error;
1717
use std::hash::Hash;
1818
use std::sync::Arc;
@@ -96,12 +96,29 @@ pub async fn handle_compare(
9696
let prev = comparison.prev(&master_commits);
9797
let next = comparison.next(&master_commits);
9898
let is_contiguous = comparison.is_contiguous(&*conn, &master_commits).await;
99+
let comparisons = comparison
100+
.statistics
101+
.into_iter()
102+
.map(|comparison| api::comparison::Comparison {
103+
benchmark: comparison.benchmark.to_string(),
104+
profile: comparison.profile.to_string(),
105+
scenario: comparison.scenario.to_string(),
106+
is_dodgy: comparison
107+
.variance
108+
.as_ref()
109+
.map(|v| v.is_dodgy())
110+
.unwrap_or(false),
111+
is_significant: comparison.is_significant(),
112+
historical_statistics: comparison.variance.map(|v| v.data),
113+
statistics: comparison.results,
114+
})
115+
.collect();
99116

100117
Ok(api::comparison::Response {
101-
variance: comparison.benchmark_variances.map(|b| b.data),
102118
prev,
103119
a: comparison.a.into(),
104120
b: comparison.b.into(),
121+
comparisons,
105122
next,
106123
is_contiguous,
107124
})
@@ -124,7 +141,7 @@ pub struct ComparisonSummary {
124141

125142
impl ComparisonSummary {
126143
pub fn summarize_comparison(comparison: &Comparison) -> Option<ComparisonSummary> {
127-
let mut benchmarks = comparison.get_benchmarks();
144+
let mut benchmarks = comparison.get_benchmarks().collect::<Vec<_>>();
128145
// Skip empty commits, sometimes happens if there's a compiler bug or so.
129146
if benchmarks.len() == 0 {
130147
return None;
@@ -141,14 +158,14 @@ impl ComparisonSummary {
141158
.min_by(|&(_, b1), &(_, b2)| cmp(b1, b2))
142159
.filter(|(_, c)| c.is_significant() && !c.is_increase())
143160
.map(|(i, _)| i);
144-
let lo = lo.map(|lo| benchmarks.remove(lo));
161+
let lo = lo.map(|lo| benchmarks.remove(lo)).cloned();
145162
let hi = benchmarks
146163
.iter()
147164
.enumerate()
148165
.max_by(|&(_, b1), &(_, b2)| cmp(b1, b2))
149166
.filter(|(_, c)| c.is_significant() && c.is_increase())
150167
.map(|(i, _)| i);
151-
let hi = hi.map(|hi| benchmarks.remove(hi));
168+
let hi = hi.map(|hi| benchmarks.remove(hi)).cloned();
152169

153170
Some(ComparisonSummary { hi, lo })
154171
}
@@ -250,11 +267,30 @@ async fn compare_given_commits(
250267
let mut responses = ctxt.statistic_series(query.clone(), aids).await?;
251268

252269
let conn = ctxt.conn().await;
270+
let statistics_for_a = statistics_from_series(&mut responses);
271+
let statistics_for_b = statistics_from_series(&mut responses);
272+
273+
let variances = BenchmarkVariances::calculate(ctxt, a.clone(), master_commits, stat).await?;
274+
let statistics = statistics_for_a
275+
.into_iter()
276+
.filter_map(|(test_case, a)| {
277+
statistics_for_b
278+
.get(&test_case)
279+
.map(|&b| BenchmarkComparison {
280+
benchmark: test_case.0,
281+
profile: test_case.1,
282+
scenario: test_case.2,
283+
variance: variances
284+
.as_ref()
285+
.and_then(|v| v.data.get(&test_case).cloned()),
286+
results: (a, b),
287+
})
288+
})
289+
.collect();
253290
Ok(Some(Comparison {
254-
benchmark_variances: BenchmarkVariances::calculate(ctxt, a.clone(), master_commits, stat)
255-
.await?,
256-
a: ArtifactData::consume_one(&*conn, a.clone(), &mut responses, master_commits).await,
257-
b: ArtifactData::consume_one(&*conn, b.clone(), &mut responses, master_commits).await,
291+
a: ArtifactDescription::for_artifact(&*conn, a.clone(), master_commits).await,
292+
b: ArtifactDescription::for_artifact(&*conn, b.clone(), master_commits).await,
293+
statistics,
258294
}))
259295
}
260296

@@ -280,37 +316,30 @@ fn previous_commits(
280316
prevs
281317
}
282318

283-
/// Data associated with a specific artifact
319+
/// Detailed description of a specific artifact
284320
#[derive(Debug, Clone)]
285-
pub struct ArtifactData {
321+
pub struct ArtifactDescription {
286322
/// The artifact in question
287323
pub artifact: ArtifactId,
288324
/// The pr of the artifact if known
289325
pub pr: Option<u32>,
290-
/// Statistics based on benchmark, profile and scenario
291-
pub statistics: StatisticsMap,
292326
/// Bootstrap data in the form "$crate" -> nanoseconds
293327
pub bootstrap: HashMap<String, u64>,
294328
}
295329

296-
type StatisticsMap = HashMap<(Benchmark, Profile, Scenario), f64>;
330+
type StatisticsMap = HashMap<TestCase, f64>;
331+
type TestCase = (Benchmark, Profile, Scenario);
297332

298-
impl ArtifactData {
333+
impl ArtifactDescription {
299334
/// For the given `ArtifactId`, consume the first datapoint in each of the given `SeriesResponse`
300335
///
301336
/// It is assumed that the provided `ArtifactId` matches the artifact id of the next data
302337
/// point for all of `SeriesResponse<T>`. If this is not true, this function will panic.
303-
async fn consume_one<'a, T>(
338+
async fn for_artifact(
304339
conn: &dyn database::Connection,
305340
artifact: ArtifactId,
306-
series: &mut [selector::SeriesResponse<T>],
307341
master_commits: &[collector::MasterCommit],
308-
) -> Self
309-
where
310-
T: Iterator<Item = (ArtifactId, Option<f64>)>,
311-
{
312-
let statistics = statistics_from_series(series);
313-
342+
) -> Self {
314343
let bootstrap = conn
315344
.get_bootstrap(&[conn.artifact_id(&artifact).await])
316345
.await;
@@ -345,7 +374,6 @@ impl ArtifactData {
345374
Self {
346375
pr,
347376
artifact,
348-
statistics,
349377
bootstrap,
350378
}
351379
}
@@ -372,16 +400,9 @@ where
372400
stats
373401
}
374402

375-
impl From<ArtifactData> for api::comparison::ArtifactData {
376-
fn from(data: ArtifactData) -> Self {
377-
let mut stats: HashMap<String, Vec<(String, f64)>> = HashMap::new();
378-
for ((benchmark, profile, scenario), value) in data.statistics {
379-
stats
380-
.entry(format!("{}-{}", benchmark, profile))
381-
.or_default()
382-
.push((scenario.to_string(), value))
383-
}
384-
api::comparison::ArtifactData {
403+
impl From<ArtifactDescription> for api::comparison::ArtifactDescription {
404+
fn from(data: ArtifactDescription) -> Self {
405+
api::comparison::ArtifactDescription {
385406
commit: match data.artifact.clone() {
386407
ArtifactId::Commit(c) => c.sha,
387408
ArtifactId::Tag(t) => t,
@@ -392,20 +413,17 @@ impl From<ArtifactData> for api::comparison::ArtifactData {
392413
None
393414
},
394415
pr: data.pr,
395-
data: stats,
396416
bootstrap: data.bootstrap,
397417
}
398418
}
399419
}
400420

401421
// A comparison of two artifacts
402422
pub struct Comparison {
403-
/// Data on how variable benchmarks have historically been
404-
///
405-
/// Is `None` if we cannot determine historical variance
406-
pub benchmark_variances: Option<BenchmarkVariances>,
407-
pub a: ArtifactData,
408-
pub b: ArtifactData,
423+
pub a: ArtifactDescription,
424+
pub b: ArtifactDescription,
425+
/// Statistics based on test case
426+
pub statistics: HashSet<BenchmarkComparison>,
409427
}
410428

411429
impl Comparison {
@@ -437,34 +455,16 @@ impl Comparison {
437455
next_commit(&self.b.artifact, master_commits).map(|c| c.sha.clone())
438456
}
439457

440-
fn get_benchmarks(&self) -> Vec<BenchmarkComparison> {
441-
let mut result = Vec::new();
442-
for (&(benchmark, profile, scenario), &a) in self.a.statistics.iter() {
443-
if profile == Profile::Doc {
444-
continue;
445-
}
446-
447-
if let Some(&b) = self.b.statistics.get(&(benchmark, profile, scenario)) {
448-
result.push(BenchmarkComparison {
449-
benchmark,
450-
profile,
451-
scenario,
452-
results: (a, b),
453-
})
454-
}
455-
}
456-
457-
result
458+
fn get_benchmarks(&self) -> impl Iterator<Item = &BenchmarkComparison> {
459+
self.statistics.iter().filter(|b| b.profile != Profile::Doc)
458460
}
459461
}
460462

461463
/// A description of the amount of variance a certain benchmark is historically
462464
/// experiencing at a given point in time.
463465
pub struct BenchmarkVariances {
464-
/// Variance data on a per benchmark basis
465-
/// Key: $benchmark-$profile-$cache
466-
/// Value: `BenchmarkVariance`
467-
pub data: HashMap<String, BenchmarkVariance>,
466+
/// Variance data on a per test case basis
467+
pub data: HashMap<(Benchmark, Profile, Scenario), BenchmarkVariance>,
468468
}
469469

470470
impl BenchmarkVariances {
@@ -493,21 +493,18 @@ impl BenchmarkVariances {
493493
.statistic_series(query, previous_commits.clone())
494494
.await?;
495495

496-
let mut variance_data: HashMap<String, BenchmarkVariance> = HashMap::new();
496+
let mut variance_data: HashMap<(Benchmark, Profile, Scenario), BenchmarkVariance> =
497+
HashMap::new();
497498
for _ in previous_commits.iter() {
498-
let series_data = statistics_from_series(&mut previous_commit_series);
499-
for ((bench, profile, scenario), value) in series_data {
500-
variance_data
501-
.entry(format!("{}-{}-{}", bench, profile, scenario))
502-
.or_default()
503-
.push(value);
499+
for (test_case, stat) in statistics_from_series(&mut previous_commit_series) {
500+
variance_data.entry(test_case).or_default().push(stat);
504501
}
505502
}
506503
if variance_data.len() < Self::MIN_PREVIOUS_COMMITS {
507504
return Ok(None);
508505
}
509506

510-
for (bench, results) in variance_data.iter_mut() {
507+
for ((bench, _, _), results) in variance_data.iter_mut() {
511508
debug!("Calculating variance for: {}", bench);
512509
results.calculate_description();
513510
}
@@ -576,6 +573,15 @@ impl BenchmarkVariance {
576573
self.description = BenchmarkVarianceDescription::Noisy(percent_change);
577574
}
578575
}
576+
577+
/// Whether we can trust this benchmark or not
578+
fn is_dodgy(&self) -> bool {
579+
matches!(
580+
self.description,
581+
BenchmarkVarianceDescription::Noisy(_)
582+
| BenchmarkVarianceDescription::HighlyVariable(_)
583+
)
584+
}
579585
}
580586

581587
#[derive(Debug, Clone, Copy, Serialize)]
@@ -626,11 +632,12 @@ pub fn next_commit<'a>(
626632
}
627633

628634
// A single comparison based on benchmark and cache state
629-
#[derive(Debug)]
635+
#[derive(Debug, Clone)]
630636
pub struct BenchmarkComparison {
631637
benchmark: Benchmark,
632638
profile: Profile,
633639
scenario: Scenario,
640+
variance: Option<BenchmarkVariance>,
634641
results: (f64, f64),
635642
}
636643

@@ -706,6 +713,22 @@ impl BenchmarkComparison {
706713
.unwrap();
707714
}
708715
}
716+
impl std::cmp::PartialEq for BenchmarkComparison {
717+
fn eq(&self, other: &Self) -> bool {
718+
self.benchmark == other.benchmark
719+
&& self.profile == other.profile
720+
&& self.scenario == other.scenario
721+
}
722+
}
723+
impl std::cmp::Eq for BenchmarkComparison {}
724+
725+
impl std::hash::Hash for BenchmarkComparison {
726+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
727+
self.benchmark.hash(state);
728+
self.profile.hash(state);
729+
self.scenario.hash(state);
730+
}
731+
}
709732

710733
// The direction of a performance change
711734
#[derive(PartialEq, Eq, Hash)]

0 commit comments

Comments
 (0)