Skip to content

Commit d1b7532

Browse files
Add relative % delta graph
This graph shows the % change from each commit to the next, which works well to show the overall noise level of a given benchmark.
1 parent bd27e5d commit d1b7532

File tree

4 files changed

+46
-33
lines changed

4 files changed

+46
-33
lines changed

site/src/api.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,26 @@ pub mod graph {
8080
pub start: Bound,
8181
pub end: Bound,
8282
pub stat: String,
83-
pub absolute: bool,
83+
pub kind: GraphKind,
84+
}
85+
86+
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
87+
#[serde(rename_all = "lowercase")]
88+
pub enum GraphKind {
89+
// Raw data
90+
Raw,
91+
// Change from the first value
92+
PercentFromFirst,
93+
// Change from the previous value, useful for looking for noise.
94+
PercentRelative,
8495
}
8596

8697
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
8798
pub struct GraphData {
8899
pub commit: u16,
89100
pub absolute: f32,
90-
pub percent: f32,
101+
// Percent change from the first datapoint shown
102+
pub percent_first: f32,
91103
pub y: f32,
92104
pub x: u64,
93105
pub is_interpolated: bool,

site/src/request_handlers/graph.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::convert::TryInto;
55
use std::str;
66
use std::sync::Arc;
77

8+
use crate::api::graph::GraphKind;
89
use crate::api::{graph, ServerResult};
910
use crate::db::{self, ArtifactId, Benchmark, Profile, Scenario};
1011
use crate::interpolate::Interpolated;
@@ -75,7 +76,7 @@ pub async fn handle_graph(
7576
start: Bound::None,
7677
end: Bound::None,
7778
stat: String::from("instructions:u"),
78-
absolute: true,
79+
kind: graph::GraphKind::Raw,
7980
};
8081

8182
if is_default_query {
@@ -106,7 +107,7 @@ pub async fn handle_graph(
106107
.into_iter()
107108
.map(|sr| {
108109
sr.interpolate()
109-
.map(|series| to_graph_data(&cc, body.absolute, series).collect::<Vec<_>>())
110+
.map(|series| to_graph_data(&cc, body.kind, series).collect::<Vec<_>>())
110111
})
111112
.collect::<Vec<_>>();
112113

@@ -176,7 +177,7 @@ pub async fn handle_graph(
176177
.collect(),
177178
)
178179
.map(|((c, d), i)| ((c, Some(d.expect("interpolated") / against)), i));
179-
let graph_data = to_graph_data(&cc, body.absolute, averaged).collect::<Vec<_>>();
180+
let graph_data = to_graph_data(&cc, body.kind, averaged).collect::<Vec<_>>();
180181
series.push(selector::SeriesResponse {
181182
path: selector::Path::new()
182183
.set(PathComponent::Benchmark("Summary".into()))
@@ -263,10 +264,11 @@ impl CommitIdxCache {
263264

264265
fn to_graph_data<'a>(
265266
cc: &'a CommitIdxCache,
266-
is_absolute: bool,
267+
kind: GraphKind,
267268
points: impl Iterator<Item = ((ArtifactId, Option<f64>), Interpolated)> + 'a,
268269
) -> impl Iterator<Item = graph::GraphData> + 'a {
269270
let mut first = None;
271+
let mut prev = None;
270272
points.map(move |((aid, point), interpolated)| {
271273
let commit = if let ArtifactId::Commit(commit) = aid {
272274
commit
@@ -276,15 +278,18 @@ fn to_graph_data<'a>(
276278
let point = point.expect("interpolated");
277279
first = Some(first.unwrap_or(point));
278280
let first = first.unwrap();
279-
let percent = (point - first) / first * 100.0;
281+
let percent_first = (point - first) / first * 100.0;
282+
let previous_point = prev.unwrap_or(point);
283+
let percent_prev = (point - previous_point) / previous_point * 100.0;
284+
prev = Some(point);
280285
graph::GraphData {
281286
commit: cc.lookup(commit.sha),
282287
absolute: point as f32,
283-
percent: percent as f32,
284-
y: if is_absolute {
285-
point as f32
286-
} else {
287-
percent as f32
288+
percent_first: percent_first as f32,
289+
y: match kind {
290+
GraphKind::Raw => point as f32,
291+
GraphKind::PercentRelative => percent_prev as f32,
292+
GraphKind::PercentFromFirst => percent_first as f32,
288293
},
289294
x: commit.date.0.timestamp() as u64 * 1000, // all dates are since 1970
290295
is_interpolated: interpolated.is_interpolated(),

site/static/index.html

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,11 @@
6262
<div id="settings">
6363
start: <input placeholder="yyyy-mm-dd or commit" id="start-bound" />
6464
end: <input placeholder="yyyy-mm-dd or commit" id="end-bound" />
65-
Absolute data: <input id='absolute' name="absolute" type="checkbox">
65+
Graph kind: <select id='kind' name="kind">
66+
<option selected="selected" value="raw">Raw</option>
67+
<option value="percentfromfirst">Percent Delta from First</option>
68+
<option value="percentrelative">Percent Delta from Previous</option>
69+
</select>
6670
<select id='stats' name="stat"></select>
6771
<a href="#" onClick="submit_settings(); return false;">Submit</a>
6872
</div>
@@ -366,7 +370,7 @@ <h3>This may take a while!</h3>
366370
isInterpolated(dataIdx) {
367371
return cacheStates.full.is_interpolated.has(dataIdx);
368372
},
369-
absoluteMode: state.absolute,
373+
absoluteMode: state.kind == "raw",
370374
});
371375

372376
let u = new uPlot(plotOpts, plotData,
@@ -415,13 +419,11 @@ <h3>This may take a while!</h3>
415419
function submit_settings() {
416420
let start = document.getElementById("start-bound").value;
417421
let end = document.getElementById("end-bound").value;
418-
let absolute = document.getElementById("absolute").checked;
419-
let stat = getSelected("stats");
420422
let params = new URLSearchParams();
421423
params.append("start", start);
422424
params.append("end", end);
423-
params.append("absolute", absolute);
424-
params.append("stat", stat);
425+
params.append("kind", getSelected("kind"));
426+
params.append("stat", getSelected("stats"));
425427
window.location.search = params.toString();
426428
}
427429

@@ -430,12 +432,12 @@ <h3>This may take a while!</h3>
430432
start: "",
431433
end: "",
432434
stat: "instructions:u",
433-
absolute: true,
435+
kind: "raw",
434436
}, state);
435437
post_json("/graph-new", values).then(prepData).then(data =>
436438
renderPlots(data, values));
437439
});
438440
</script>
439441
</body>
440442

441-
</html>
443+
</html>

site/static/shared.js

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ function getSelected(name) {
3838
function setSelected(id, text) {
3939
let e = document.getElementById(id);
4040
for (let i = 0; i < e.options.length; i++) {
41-
if (e.options[i].text === text) {
41+
e.options[i].selected = false;
42+
}
43+
for (let i = 0; i < e.options.length; i++) {
44+
if (e.options[i].text === text || e.options[i].value == text) {
4245
e.options[i].selected = true;
4346
return;
4447
}
@@ -192,9 +195,6 @@ function loadState(callback, skip_settings) {
192195
for (let param of params) {
193196
let key = param[0];
194197
let value = param[1];
195-
if (key == "absolute") {
196-
value = value == "true" ? true : false;
197-
}
198198
state[key] = value;
199199
}
200200
if (state.start) {
@@ -203,20 +203,14 @@ function loadState(callback, skip_settings) {
203203
if (state.end) {
204204
document.getElementById("end-bound").value = state.end;
205205
}
206-
if (state.absolute === true || state.absolute === false) {
207-
document.getElementById("absolute").checked = state.absolute;
208-
} else {
209-
// check absolute by default
210-
let element = document.getElementById("absolute");
211-
if (element) {
212-
element.checked = true;
213-
}
214-
}
215206
if (!skip_settings) {
216207
make_settings(() => {
217208
if (state.stat) {
218209
setSelected("stats", state.stat);
219210
}
211+
if (state.kind) {
212+
setSelected("kind", state.kind);
213+
}
220214
callback(state);
221215
});
222216
} else {

0 commit comments

Comments
 (0)