Skip to content

Commit 265aa94

Browse files
authored
Merge pull request #1050 from rylev/refactor-graph
Refactorings: graph => graphs
2 parents b0467dc + 191df8c commit 265aa94

File tree

7 files changed

+117
-81
lines changed

7 files changed

+117
-81
lines changed

site/src/api.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ pub mod graph {
7070
// y-values
7171
pub points: Vec<f32>,
7272
// The index of interpolated coordinates
73-
pub is_interpolated: HashSet<u16>,
73+
pub interpolated_indices: HashSet<u16>,
7474
}
7575

7676
#[derive(Debug, PartialEq, Clone, Serialize)]

site/src/average.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ mod tests {
110110
fn test_interpolation_average() {
111111
// Test that averaging works with interpolation.
112112
use crate::db::Point;
113-
use crate::interpolate::{Interpolate, Interpolated};
113+
use crate::interpolate::{Interpolate, IsInterpolated};
114114

115115
let v = vec![
116116
vec![("a", Some(0.0)), ("b", Some(200.0))],
@@ -125,11 +125,11 @@ mod tests {
125125
let mut average = average(iterators);
126126

127127
let a = average.next().unwrap();
128-
assert_eq!(a, (("a", Some(50.0)), Interpolated::No));
128+
assert_eq!(a, (("a", Some(50.0)), IsInterpolated::No));
129129
assert_eq!(a.interpolated(), false);
130130

131131
let b = average.next().unwrap();
132-
assert_eq!(b, (("b", Some(150.0)), Interpolated::Yes));
132+
assert_eq!(b, (("b", Some(150.0)), IsInterpolated::Yes));
133133
assert_eq!(b.interpolated(), true);
134134

135135
assert!(average.next().is_none());

site/src/interpolate.rs

+57-32
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,27 @@
55
//! mostly want to avoid dropping or improving summary performance when data
66
//! points are missing as that's misleading and noisy, and this works well for
77
//! that.
8+
//!
9+
//! As an example:
10+
//! Given a series with some missing data `[1, 2, ?, 4]`,
11+
//! this iterator yields `[1, 2, 2, 4]`.
812
913
use crate::db::Point;
1014

15+
/// Whether a point has been interpolated or not
1116
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
12-
pub enum Interpolated {
17+
pub enum IsInterpolated {
1318
No,
1419
Yes,
1520
}
1621

17-
impl Interpolated {
18-
pub fn is_interpolated(self) -> bool {
19-
self == Interpolated::Yes
22+
impl IsInterpolated {
23+
pub fn as_bool(self) -> bool {
24+
self == IsInterpolated::Yes
2025
}
2126
}
2227

23-
impl<P> Point for (P, Interpolated)
28+
impl<P> Point for (P, IsInterpolated)
2429
where
2530
P: Point,
2631
{
@@ -39,21 +44,23 @@ where
3944
self.0.set_value(value)
4045
}
4146
fn interpolated(&self) -> bool {
42-
self.1.is_interpolated()
47+
self.1.as_bool()
4348
}
4449
fn set_interpolated(&mut self) {
45-
self.1 = Interpolated::Yes;
50+
self.1 = IsInterpolated::Yes;
4651
}
4752
}
4853

54+
/// The interpolated iterator
4955
pub struct Interpolate<I>
5056
where
5157
I: Iterator,
5258
{
59+
/// The base iterator we're interpolating
5360
iterator: I,
61+
/// The last seen point which will be used for interpolation
5462
last_seen: Option<f64>,
55-
56-
// When we need to seek forward at the start, we store things in here.
63+
/// When we need to seek forward at the start, we store things in here.
5764
consumed: Vec<I::Item>,
5865
}
5966

@@ -76,15 +83,15 @@ where
7683
I: Iterator,
7784
I::Item: Point,
7885
{
79-
type Item = (I::Item, Interpolated);
86+
type Item = (I::Item, IsInterpolated);
8087

8188
fn next(&mut self) -> Option<Self::Item> {
8289
if let Some(mut item) = self.consumed.pop() {
8390
item.set_value(self.last_seen.unwrap());
8491
let interpolation = if self.consumed.is_empty() {
85-
Interpolated::No
92+
IsInterpolated::No
8693
} else {
87-
Interpolated::Yes
94+
IsInterpolated::Yes
8895
};
8996
return Some((item, interpolation));
9097
}
@@ -94,12 +101,12 @@ where
94101
match item.value() {
95102
Some(pt) => {
96103
self.last_seen = Some(pt);
97-
return Some((item, Interpolated::No));
104+
return Some((item, IsInterpolated::No));
98105
}
99106
None => {
100107
if let Some(last) = self.last_seen {
101108
item.set_value(last);
102-
return Some((item, Interpolated::Yes));
109+
return Some((item, IsInterpolated::Yes));
103110
}
104111

105112
self.consumed.push(item);
@@ -122,7 +129,7 @@ where
122129

123130
let mut item = self.consumed.pop().unwrap();
124131
item.set_value(self.last_seen.unwrap());
125-
return Some((item, Interpolated::Yes));
132+
return Some((item, IsInterpolated::Yes));
126133
}
127134
}
128135
}
@@ -139,15 +146,15 @@ where
139146

140147
#[cfg(test)]
141148
mod tests {
142-
use super::{Interpolate, Interpolated};
149+
use super::{Interpolate, IsInterpolated};
143150

144151
#[test]
145152
fn test_no_interpolation() {
146153
let v = vec![("a", 1.0), ("b", 2.0)];
147154
let mut iter = Interpolate::new(v.into_iter());
148155

149-
assert_eq!(iter.next().unwrap(), (("a", 1.0), Interpolated::No));
150-
assert_eq!(iter.next().unwrap(), (("b", 2.0), Interpolated::No));
156+
assert_eq!(iter.next().unwrap(), (("a", 1.0), IsInterpolated::No));
157+
assert_eq!(iter.next().unwrap(), (("b", 2.0), IsInterpolated::No));
151158
assert!(iter.next().is_none());
152159
}
153160

@@ -156,10 +163,16 @@ mod tests {
156163
let v = vec![("a", None), ("b", None), ("c", Some(3.0)), ("d", Some(4.0))];
157164
let mut iter = Interpolate::new(v.into_iter());
158165

159-
assert_eq!(iter.next().unwrap(), (("a", Some(3.0)), Interpolated::Yes));
160-
assert_eq!(iter.next().unwrap(), (("b", Some(3.0)), Interpolated::Yes));
161-
assert_eq!(iter.next().unwrap(), (("c", Some(3.0)), Interpolated::No));
162-
assert_eq!(iter.next().unwrap(), (("d", Some(4.0)), Interpolated::No));
166+
assert_eq!(
167+
iter.next().unwrap(),
168+
(("a", Some(3.0)), IsInterpolated::Yes)
169+
);
170+
assert_eq!(
171+
iter.next().unwrap(),
172+
(("b", Some(3.0)), IsInterpolated::Yes)
173+
);
174+
assert_eq!(iter.next().unwrap(), (("c", Some(3.0)), IsInterpolated::No));
175+
assert_eq!(iter.next().unwrap(), (("d", Some(4.0)), IsInterpolated::No));
163176
assert!(iter.next().is_none());
164177
}
165178

@@ -175,12 +188,18 @@ mod tests {
175188
];
176189
let mut iter = Interpolate::new(v.into_iter());
177190

178-
assert_eq!(iter.next().unwrap(), (("a", Some(1.0)), Interpolated::No));
179-
assert_eq!(iter.next().unwrap(), (("b", Some(2.0)), Interpolated::No));
180-
assert_eq!(iter.next().unwrap(), (("c", Some(2.0)), Interpolated::Yes));
181-
assert_eq!(iter.next().unwrap(), (("d", Some(2.0)), Interpolated::Yes));
182-
assert_eq!(iter.next().unwrap(), (("e", Some(5.0)), Interpolated::No));
183-
assert_eq!(iter.next().unwrap(), (("f", Some(6.0)), Interpolated::No));
191+
assert_eq!(iter.next().unwrap(), (("a", Some(1.0)), IsInterpolated::No));
192+
assert_eq!(iter.next().unwrap(), (("b", Some(2.0)), IsInterpolated::No));
193+
assert_eq!(
194+
iter.next().unwrap(),
195+
(("c", Some(2.0)), IsInterpolated::Yes)
196+
);
197+
assert_eq!(
198+
iter.next().unwrap(),
199+
(("d", Some(2.0)), IsInterpolated::Yes)
200+
);
201+
assert_eq!(iter.next().unwrap(), (("e", Some(5.0)), IsInterpolated::No));
202+
assert_eq!(iter.next().unwrap(), (("f", Some(6.0)), IsInterpolated::No));
184203
assert!(iter.next().is_none());
185204
}
186205

@@ -189,10 +208,16 @@ mod tests {
189208
let v = vec![("a", Some(1.0)), ("b", Some(2.0)), ("c", None), ("d", None)];
190209
let mut iter = Interpolate::new(v.into_iter());
191210

192-
assert_eq!(iter.next().unwrap(), (("a", Some(1.0)), Interpolated::No));
193-
assert_eq!(iter.next().unwrap(), (("b", Some(2.0)), Interpolated::No));
194-
assert_eq!(iter.next().unwrap(), (("c", Some(2.0)), Interpolated::Yes));
195-
assert_eq!(iter.next().unwrap(), (("d", Some(2.0)), Interpolated::Yes));
211+
assert_eq!(iter.next().unwrap(), (("a", Some(1.0)), IsInterpolated::No));
212+
assert_eq!(iter.next().unwrap(), (("b", Some(2.0)), IsInterpolated::No));
213+
assert_eq!(
214+
iter.next().unwrap(),
215+
(("c", Some(2.0)), IsInterpolated::Yes)
216+
);
217+
assert_eq!(
218+
iter.next().unwrap(),
219+
(("d", Some(2.0)), IsInterpolated::Yes)
220+
);
196221
assert!(iter.next().is_none());
197222
}
198223
}

site/src/request_handlers.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mod status_page;
99
pub use bootstrap::handle_bootstrap;
1010
pub use dashboard::handle_dashboard;
1111
pub use github::handle_github;
12-
pub use graph::handle_graph;
12+
pub use graph::handle_graphs;
1313
pub use next_commit::handle_next_commit;
1414
pub use self_profile::{
1515
handle_self_profile, handle_self_profile_processed_download, handle_self_profile_raw,

site/src/request_handlers/graph.rs

+43-33
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ use std::sync::Arc;
55
use crate::api::graph::GraphKind;
66
use crate::api::{graph, ServerResult};
77
use crate::db::{self, ArtifactId, Benchmark, Profile, Scenario};
8-
use crate::interpolate::Interpolated;
8+
use crate::interpolate::IsInterpolated;
99
use crate::load::SiteCtxt;
1010
use crate::selector::{Query, Selector, SeriesResponse, Tag};
1111

12-
pub async fn handle_graph(
12+
pub async fn handle_graphs(
1313
body: graph::Request,
1414
ctxt: &SiteCtxt,
1515
) -> ServerResult<Arc<graph::Response>> {
@@ -43,9 +43,9 @@ async fn graph_response(
4343
body: graph::Request,
4444
ctxt: &SiteCtxt,
4545
) -> ServerResult<Arc<graph::Response>> {
46-
let range = ctxt.data_range(body.start.clone()..=body.end.clone());
47-
let commits: Arc<Vec<_>> = Arc::new(range.into_iter().map(|c| c.clone().into()).collect());
48-
let metric_selector = Selector::One(body.stat.clone());
46+
let range = ctxt.data_range(body.start..=body.end);
47+
let commits: Arc<Vec<_>> = Arc::new(range.into_iter().map(|c| c.into()).collect());
48+
let metric_selector = Selector::One(body.stat);
4949
let mut benchmarks = HashMap::new();
5050

5151
let series_iterator = ctxt
@@ -65,7 +65,7 @@ async fn graph_response(
6565
let benchmark = series_response.path.get::<Benchmark>()?.to_string();
6666
let profile = *series_response.path.get::<Profile>()?;
6767
let scenario = series_response.path.get::<Scenario>()?.to_string();
68-
let graph_series = graph_series(body.kind, series_response.series);
68+
let graph_series = graph_series(series_response.series, body.kind);
6969

7070
benchmarks
7171
.entry(benchmark)
@@ -75,14 +75,37 @@ async fn graph_response(
7575
.insert(scenario, graph_series);
7676
}
7777

78+
let summary_benchmark = create_summary(ctxt, metric_selector, &commits, body.kind).await?;
79+
80+
benchmarks.insert("Summary".to_string(), summary_benchmark);
81+
82+
Ok(Arc::new(graph::Response {
83+
commits: Arc::try_unwrap(commits)
84+
.unwrap()
85+
.into_iter()
86+
.map(|c| match c {
87+
ArtifactId::Commit(c) => (c.date.0.timestamp(), c.sha),
88+
ArtifactId::Tag(_) => unreachable!(),
89+
})
90+
.collect(),
91+
benchmarks,
92+
}))
93+
}
94+
95+
/// Creates a summary "benchmark" that averages the results of all other
96+
/// test cases per profile type
97+
async fn create_summary(
98+
ctxt: &SiteCtxt,
99+
metric_selector: Selector<String>,
100+
commits: &Arc<Vec<ArtifactId>>,
101+
graph_kind: GraphKind,
102+
) -> ServerResult<HashMap<Profile, HashMap<String, graph::Series>>> {
78103
let mut baselines = HashMap::new();
79104
let mut summary_benchmark = HashMap::new();
80-
81105
let summary_query_cases = iproduct!(
82106
ctxt.summary_scenarios(),
83107
vec![Profile::Check, Profile::Debug, Profile::Opt]
84108
);
85-
86109
for (scenario, profile) in summary_query_cases {
87110
let query = Query::new()
88111
.set::<String>(Tag::Benchmark, Selector::All)
@@ -121,43 +144,30 @@ async fn graph_response(
121144
)
122145
.map(|((c, d), i)| ((c, Some(d.expect("interpolated") / baseline)), i));
123146

124-
let graph_series = graph_series(body.kind, avg_vs_baseline);
147+
let graph_series = graph_series(avg_vs_baseline, graph_kind);
125148

126149
summary_benchmark
127150
.entry(profile)
128151
.or_insert_with(HashMap::new)
129152
.insert(scenario.to_string(), graph_series);
130153
}
131-
132-
benchmarks.insert("Summary".to_string(), summary_benchmark);
133-
134-
Ok(Arc::new(graph::Response {
135-
commits: Arc::try_unwrap(commits)
136-
.unwrap()
137-
.into_iter()
138-
.map(|c| match c {
139-
ArtifactId::Commit(c) => (c.date.0.timestamp(), c.sha),
140-
ArtifactId::Tag(_) => unreachable!(),
141-
})
142-
.collect(),
143-
benchmarks,
144-
}))
154+
Ok(summary_benchmark)
145155
}
146156

147157
fn graph_series(
158+
points: impl Iterator<Item = ((ArtifactId, Option<f64>), IsInterpolated)>,
148159
kind: GraphKind,
149-
points: impl Iterator<Item = ((ArtifactId, Option<f64>), Interpolated)>,
150160
) -> graph::Series {
151161
let mut graph_series = graph::Series {
152162
points: Vec::new(),
153-
is_interpolated: Default::default(),
163+
interpolated_indices: Default::default(),
154164
};
155165

156166
let mut first = None;
157167
let mut prev = None;
158168

159-
for (idx, ((_aid, point), interpolated)) in points.enumerate() {
160-
let point = point.expect("interpolated");
169+
for (idx, ((_aid, point), is_interpolated)) in points.enumerate() {
170+
let point = point.expect("interpolated point still produced an empty value");
161171
first = Some(first.unwrap_or(point));
162172
let first = first.unwrap();
163173
let percent_first = (point - first) / first * 100.0;
@@ -166,15 +176,15 @@ fn graph_series(
166176
prev = Some(point);
167177

168178
let value = match kind {
169-
GraphKind::Raw => point as f32,
170-
GraphKind::PercentRelative => percent_prev as f32,
171-
GraphKind::PercentFromFirst => percent_first as f32,
172-
};
179+
GraphKind::Raw => point,
180+
GraphKind::PercentRelative => percent_prev,
181+
GraphKind::PercentFromFirst => percent_first,
182+
} as f32;
173183

174184
graph_series.points.push(value);
175185

176-
if interpolated.is_interpolated() {
177-
graph_series.is_interpolated.insert(idx as u16);
186+
if is_interpolated.as_bool() {
187+
graph_series.interpolated_indices.insert(idx as u16);
178188
}
179189
}
180190

site/src/server.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -403,16 +403,12 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
403403
"/perf/self-profile-raw" => Ok(to_response(
404404
request_handlers::handle_self_profile_raw(check!(parse_body(&body)), &ctxt).await,
405405
)),
406-
"/perf/graph" => Ok(
407-
match request_handlers::handle_graph(check!(parse_body(&body)), &ctxt).await {
406+
"/perf/graphs" => Ok(
407+
match request_handlers::handle_graphs(check!(parse_body(&body)), &ctxt).await {
408408
Ok(result) => {
409-
let mut response = http::Response::builder()
409+
let response = http::Response::builder()
410410
.header_typed(ContentType::json())
411411
.header_typed(CacheControl::new().with_no_cache().with_no_store());
412-
response.headers_mut().unwrap().insert(
413-
hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN,
414-
hyper::header::HeaderValue::from_static("*"),
415-
);
416412
let body = serde_json::to_vec(&result).unwrap();
417413
response.body(hyper::Body::from(body)).unwrap()
418414
}

0 commit comments

Comments
 (0)