Skip to content

Commit 1f9570b

Browse files
committed
Make single query for graph data
Make one query for graph data instead of sixteen. With local SQLite DB, this reduces the time to handle a request for 8 months of data from 8 seconds to 3. Hoping that the results are even better for Postgres, as currently, such a request causes a timeout on perf.rust-lang.org.
1 parent 265aa94 commit 1f9570b

File tree

1 file changed

+41
-46
lines changed

1 file changed

+41
-46
lines changed

site/src/request_handlers/graph.rs

Lines changed: 41 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -45,27 +45,31 @@ async fn graph_response(
4545
) -> ServerResult<Arc<graph::Response>> {
4646
let range = ctxt.data_range(body.start..=body.end);
4747
let commits: Arc<Vec<_>> = Arc::new(range.into_iter().map(|c| c.into()).collect());
48-
let metric_selector = Selector::One(body.stat);
4948
let mut benchmarks = HashMap::new();
5049

51-
let series_iterator = ctxt
50+
let interpolated_responses: Vec<_> = ctxt
5251
.statistic_series(
5352
Query::new()
5453
.set::<String>(Tag::Benchmark, Selector::All)
5554
.set::<String>(Tag::Profile, Selector::All)
5655
.set::<String>(Tag::Scenario, Selector::All)
57-
.set::<String>(Tag::Metric, metric_selector.clone()),
56+
.set::<String>(Tag::Metric, Selector::One(body.stat)),
5857
commits.clone(),
5958
)
6059
.await?
6160
.into_iter()
62-
.map(SeriesResponse::interpolate);
61+
.map(|sr| sr.interpolate().map(|series| series.collect::<Vec<_>>()))
62+
.collect();
6363

64-
for series_response in series_iterator {
65-
let benchmark = series_response.path.get::<Benchmark>()?.to_string();
66-
let profile = *series_response.path.get::<Profile>()?;
67-
let scenario = series_response.path.get::<Scenario>()?.to_string();
68-
let graph_series = graph_series(series_response.series, body.kind);
64+
let summary_benchmark = create_summary(ctxt, &interpolated_responses, body.kind)?;
65+
66+
benchmarks.insert("Summary".to_string(), summary_benchmark);
67+
68+
for response in interpolated_responses {
69+
let benchmark = response.path.get::<Benchmark>()?.to_string();
70+
let profile = *response.path.get::<Profile>()?;
71+
let scenario = response.path.get::<Scenario>()?.to_string();
72+
let graph_series = graph_series(response.series.into_iter(), body.kind);
6973

7074
benchmarks
7175
.entry(benchmark)
@@ -75,10 +79,6 @@ async fn graph_response(
7579
.insert(scenario, graph_series);
7680
}
7781

78-
let summary_benchmark = create_summary(ctxt, metric_selector, &commits, body.kind).await?;
79-
80-
benchmarks.insert("Summary".to_string(), summary_benchmark);
81-
8282
Ok(Arc::new(graph::Response {
8383
commits: Arc::try_unwrap(commits)
8484
.unwrap()
@@ -94,10 +94,9 @@ async fn graph_response(
9494

9595
/// Creates a summary "benchmark" that averages the results of all other
9696
/// test cases per profile type
97-
async fn create_summary(
97+
fn create_summary(
9898
ctxt: &SiteCtxt,
99-
metric_selector: Selector<String>,
100-
commits: &Arc<Vec<ArtifactId>>,
99+
interpolated_responses: &[SeriesResponse<Vec<((ArtifactId, Option<f64>), IsInterpolated)>>],
101100
graph_kind: GraphKind,
102101
) -> ServerResult<HashMap<Profile, HashMap<String, graph::Series>>> {
103102
let mut baselines = HashMap::new();
@@ -107,42 +106,38 @@ async fn create_summary(
107106
vec![Profile::Check, Profile::Debug, Profile::Opt]
108107
);
109108
for (scenario, profile) in summary_query_cases {
110-
let query = Query::new()
111-
.set::<String>(Tag::Benchmark, Selector::All)
112-
.set(Tag::Profile, Selector::One(profile))
113-
.set(Tag::Scenario, Selector::One(scenario))
114-
.set(Tag::Metric, metric_selector.clone());
115-
116-
let baseline_query = Query::new()
117-
.set::<String>(Tag::Benchmark, Selector::All)
118-
.set(Tag::Profile, Selector::One(profile))
119-
.set(Tag::Scenario, Selector::One(Scenario::Empty))
120-
.set(Tag::Metric, metric_selector.clone());
121-
122-
let baseline = match baselines.entry(baseline_query.clone()) {
109+
let baseline = match baselines.entry((profile, scenario)) {
123110
std::collections::hash_map::Entry::Occupied(o) => *o.get(),
124111
std::collections::hash_map::Entry::Vacant(v) => {
125-
let value = db::average(
126-
ctxt.statistic_series(baseline_query, commits.clone())
127-
.await?
128-
.into_iter()
129-
.map(|sr| sr.interpolate().series)
130-
.collect::<Vec<_>>(),
131-
)
132-
.next()
133-
.map_or(0.0, |((_c, d), _interpolated)| d.expect("interpolated"));
112+
let baseline_responses = interpolated_responses
113+
.iter()
114+
.filter(|sr| {
115+
let p = sr.path.get::<Profile>().unwrap();
116+
let s = sr.path.get::<Scenario>().unwrap();
117+
*p == profile && *s == Scenario::Empty
118+
})
119+
.map(|sr| sr.series.iter().cloned())
120+
.collect();
121+
122+
let value = db::average(baseline_responses)
123+
.next()
124+
.map_or(0.0, |((_c, d), _interpolated)| d.expect("interpolated"));
134125
*v.insert(value)
135126
}
136127
};
137128

138-
let avg_vs_baseline = db::average(
139-
ctxt.statistic_series(query.clone(), commits.clone())
140-
.await?
141-
.into_iter()
142-
.map(|sr| sr.interpolate().series)
143-
.collect(),
144-
)
145-
.map(|((c, d), i)| ((c, Some(d.expect("interpolated") / baseline)), i));
129+
let summary_case_responses = interpolated_responses
130+
.iter()
131+
.filter(|sr| {
132+
let p = sr.path.get::<Profile>().unwrap();
133+
let s = sr.path.get::<Scenario>().unwrap();
134+
*p == profile && *s == scenario
135+
})
136+
.map(|sr| sr.series.iter().cloned())
137+
.collect();
138+
139+
let avg_vs_baseline = db::average(summary_case_responses)
140+
.map(|((c, d), i)| ((c, Some(d.expect("interpolated") / baseline)), i));
146141

147142
let graph_series = graph_series(avg_vs_baseline, graph_kind);
148143

0 commit comments

Comments
 (0)