Skip to content

Commit e8a20e2

Browse files
committed
Add new summary table to compare page
1 parent d00760d commit e8a20e2

File tree

3 files changed

+197
-117
lines changed

3 files changed

+197
-117
lines changed

site/static/compare.html

Lines changed: 7 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ <h2>Comparing <span id="stat-header">{{stat}}</span> between <span id="before">{
262262
</span>
263263
</span>
264264
</div>
265-
<input type="checkbox" v-model="filter.showNonRelevant" style="margin-left: 20px;" />
265+
<input type="checkbox" v-model="filter.nonRelevant" style="margin-left: 20px;" />
266266
</div>
267267
<div class="section">
268268
<div class="section-heading"><span>Display raw data</span>
@@ -278,49 +278,17 @@ <h2>Comparing <span id="stat-header">{{stat}}</span> between <span id="before">{
278278
</fieldset>
279279
<p v-if="dataLoading && !data">Loading ...</p>
280280
<div v-if="data" id="content" style="margin-top: 15px">
281-
<div id="summary">
282-
<div style="display: flex; justify-content: end;">
281+
<div id="main-summary">
282+
<summary-table :cases="filteredSummary"></summary-table>
283+
<div style="position: absolute; right: 5px; top: 5px;">
283284
<span class="tooltip" style="margin-right: 1em;">?
284285
<span class="tooltiptext">
285-
The percents show arithmetic mean amongst regressions, amongst improvements
286-
and finally amongst all benchmarks in each category (all benchmarks or
287-
filtered benchmarks).
286+
The table shows summaries of regressions, improvements and all changes
287+
calculated from data that is currently visible (data that passes the
288+
active filters).
288289
</span>
289290
</span>
290291
</div>
291-
<div v-for="summaryPair in Object.entries(summary)" class="summary-container">
292-
<span style="font-weight: bold; margin-left: 5px; text-transform: capitalize;">{{
293-
summaryPair[0] }}:</span>
294-
<div class="summary-values">
295-
<span class="summary summary-wide positive">
296-
{{summaryPair[1].regressions.toString().padStart(3, "&nbsp;")}}
297-
<svg style="width:18px;height:18px" viewBox="0 0 24 24">
298-
<path
299-
d="M16,6L18.29,8.29L13.41,13.17L9.41,9.17L2,16.59L3.41,18L9.41,12L13.41,16L19.71,9.71L22,12V6H16Z">
300-
</path>
301-
</svg>
302-
&nbsp;(+{{(summaryPair[1].regressions_avg).toFixed(2)}}%)
303-
</span>
304-
<span class="summary">
305-
{{summaryPair[1].unchanged.toString().padStart(3, "&nbsp;")}}
306-
<svg style="width:18px;height:18px" viewBox="0 0 24 24">
307-
<path d="M22,12L18,8V11H3V13H18V16L22,12Z"></path>
308-
</svg>
309-
</span>
310-
<span class="summary summary-wide negative">
311-
{{summaryPair[1].improvements.toString().padStart(3, "&nbsp;")}}
312-
<svg style="width:18px;height:18px" viewBox="0 0 24 24">
313-
<path
314-
d="M16,18L18.29,15.71L13.41,10.83L9.41,14.83L2,7.41L3.41,6L9.41,12L13.41,8L19.71,14.29L22,12V18H16Z">
315-
</path>
316-
</svg>
317-
&nbsp;({{(summaryPair[1].improvements_avg).toFixed(2)}}%)
318-
</span>
319-
<span class="summary" v-bind:class="percentClass(summaryPair[1].average)">
320-
&nbsp;{{ signIfPositive(summaryPair[1].average) }}{{ (summaryPair[1].average).toFixed(2) }}%
321-
</span>
322-
</div>
323-
</div>
324292
</div>
325293
<div v-if="data.new_errors.length">
326294
<p><b>Newly broken benchmarks</b>:</p>

site/static/compare/script.js

Lines changed: 177 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,29 @@ function findQueryParam(name) {
66
}
77
}
88

9+
function createDefaultFilter() {
10+
return {
11+
name: null,
12+
nonRelevant: false,
13+
profile: {
14+
check: true,
15+
debug: true,
16+
opt: true,
17+
doc: true
18+
},
19+
scenario: {
20+
full: true,
21+
incrFull: true,
22+
incrUnchanged: true,
23+
incrPatched: true
24+
},
25+
category: {
26+
primary: true,
27+
secondary: true
28+
}
29+
};
30+
}
31+
932
const app = Vue.createApp({
1033
mounted() {
1134
const app = this;
@@ -20,26 +43,7 @@ const app = Vue.createApp({
2043
},
2144
data() {
2245
return {
23-
filter: {
24-
name: null,
25-
showNonRelevant: false,
26-
profile: {
27-
check: true,
28-
debug: true,
29-
opt: true,
30-
doc: true
31-
},
32-
scenario: {
33-
full: true,
34-
incrFull: true,
35-
incrUnchanged: true,
36-
incrPatched: true
37-
},
38-
category: {
39-
primary: true,
40-
secondary: true
41-
}
42-
},
46+
filter: createDefaultFilter(),
4347
showRawData: false,
4448
data: null,
4549
dataLoading: false
@@ -103,7 +107,7 @@ const app = Vue.createApp({
103107
let nameFilter = filter.name && filter.name.trim();
104108
nameFilter = !nameFilter || name.includes(nameFilter);
105109

106-
const relevanceFilter = filter.showNonRelevant ? true : testCase.isRelevant;
110+
const relevanceFilter = filter.nonRelevant ? true : testCase.isRelevant;
107111

108112
return (
109113
profileFilter(testCase.profile) &&
@@ -219,56 +223,9 @@ const app = Vue.createApp({
219223
stat() {
220224
return findQueryParam("stat") || "instructions:u";
221225
},
222-
summary() {
223-
// Create object with each test case that is not filtered out as a key
224-
const filtered = this.testCases.reduce((sum, next) => {
225-
sum[testCaseString(next)] = true;
226-
return sum;
227-
}, {});
228-
const newCount = {
229-
regressions: 0,
230-
regressions_avg: 0,
231-
improvements: 0,
232-
improvements_avg: 0,
233-
unchanged: 0,
234-
average: 0
235-
};
236-
237-
const addDatum = (result, datum, percent) => {
238-
if (percent > 0 && datum.is_relevant) {
239-
result.regressions += 1;
240-
result.regressions_avg += percent;
241-
} else if (percent < 0 && datum.is_relevant) {
242-
result.improvements += 1;
243-
result.improvements_avg += percent;
244-
} else {
245-
result.unchanged += 1;
246-
}
247-
result.average += percent;
248-
};
249-
250-
let result = {all: {...newCount}, filtered: {...newCount}}
251-
for (let d of this.data.comparisons) {
252-
const testCase = testCaseString(d)
253-
const datumA = d.statistics[0];
254-
const datumB = d.statistics[1];
255-
let percent = 100 * ((datumB - datumA) / datumA);
256-
addDatum(result.all, d, percent);
257-
if (filtered[testCase]) {
258-
addDatum(result.filtered, d, percent);
259-
}
260-
}
261-
262-
const computeAvg = (result) => {
263-
result.improvements_avg /= Math.max(result.improvements, 1);
264-
result.regressions_avg /= Math.max(result.regressions, 1);
265-
result.average /= Math.max(result.regressions + result.improvements + result.unchanged, 1);
266-
};
267-
computeAvg(result.all);
268-
computeAvg(result.filtered);
269-
270-
return result;
271-
226+
// Returns summary of currently filtered data
227+
filteredSummary() {
228+
return computeSummary(this.testCases);
272229
},
273230
benchmarkMap() {
274231
if (!this.data) return {};
@@ -289,12 +246,6 @@ const app = Vue.createApp({
289246
prLink(pr) {
290247
return `https://github.com/rust-lang/rust/pull/${pr}`;
291248
},
292-
signIfPositive(pct) {
293-
if (pct >= 0) {
294-
return "+";
295-
}
296-
return "";
297-
},
298249
diffClass(diff) {
299250
let klass = "";
300251
if (diff > 1) {
@@ -433,6 +384,86 @@ app.component('test-cases-table', {
433384
</div>
434385
`
435386
});
387+
388+
const SummaryPercentValue = {
389+
props: {
390+
value: Number,
391+
padWidth: {
392+
type: Number,
393+
default: null
394+
}
395+
},
396+
template: `
397+
<span><span v-html="padSpaces" />{{ formattedValue }}%</span>
398+
`,
399+
computed: {
400+
formattedValue() {
401+
return `${this.signIfPositive(this.value)}${this.value.toFixed(2)}`;
402+
},
403+
padSpaces() {
404+
let value = this.formattedValue;
405+
if (value.length < this.padWidth) {
406+
return "&nbsp;".repeat(this.padWidth - value.length);
407+
}
408+
return "";
409+
}
410+
}
411+
};
412+
const SummaryRange = {
413+
props: {
414+
range: Array,
415+
},
416+
template: `
417+
<div v-if="range.length > 0">
418+
[<SummaryPercentValue :value="range[0]" :padWidth="6" />, <SummaryPercentValue :value="range[1]" :padWidth="6" />]
419+
</div>
420+
<div v-else>-</div>
421+
`, components: {SummaryPercentValue}
422+
};
423+
const SummaryCount = {
424+
props: {
425+
cases: Number,
426+
benchmarks: Number
427+
},
428+
template: `
429+
<span :title="cases + ' test case(s), ' + benchmarks + ' unique benchmark(s)'">{{ cases }} ({{ benchmarks }})</span>
430+
`
431+
};
432+
433+
app.component('summary-table', {
434+
props: ['cases'],
435+
template: `
436+
<table class="summary-table">
437+
<thead>
438+
<th><!-- icon --></th>
439+
<th>Range</th>
440+
<th>Mean</th>
441+
<th>Count</th>
442+
</thead>
443+
<tbody>
444+
<tr class="positive">
445+
<td title="Regresions">❌</td>
446+
<td><SummaryRange :range="cases.regressions.range" /></td>
447+
<td><SummaryPercentValue :value="cases.regressions.average" /></td>
448+
<td><SummaryCount :cases="cases.regressions.count" :benchmarks="cases.regressions.benchmarks" /></td>
449+
</tr>
450+
<tr class="negative">
451+
<td title="Improvements">✅</td>
452+
<td><SummaryRange :range="cases.improvements.range" /></td>
453+
<td><SummaryPercentValue :value="cases.improvements.average" /></td>
454+
<td><SummaryCount :cases="cases.improvements.count" :benchmarks="cases.improvements.benchmarks" /></td>
455+
</tr>
456+
<tr>
457+
<td title="All changes">❌,✅</td>
458+
<td><SummaryRange :range="cases.all.range" /></td>
459+
<td><SummaryPercentValue :value="cases.all.average" /></td>
460+
<td><SummaryCount :cases="cases.all.count" :benchmarks="cases.all.benchmarks" /></td>
461+
</tr>
462+
</tbody>
463+
</table>
464+
`,
465+
components: {SummaryRange, SummaryPercentValue, SummaryCount}
466+
});
436467
app.mixin({
437468
methods: {
438469
percentClass(pct) {
@@ -448,6 +479,12 @@ app.mixin({
448479
}
449480
return klass;
450481
},
482+
signIfPositive(pct) {
483+
if (pct >= 0) {
484+
return "+";
485+
}
486+
return "";
487+
},
451488
}
452489
});
453490

@@ -470,6 +507,69 @@ function testCaseString(testCase) {
470507
return testCase.benchmark + "-" + testCase.profile + "-" + testCase.scenario;
471508
}
472509

510+
/**
511+
* Computes summaries of improvements, regressions and all changes from the given `comparisons`.
512+
* Returns a dictionary {improvements, regressions, all}.
513+
*/
514+
function computeSummary(testCases) {
515+
const regressions = {
516+
values: [],
517+
benchmarks: new Set(),
518+
};
519+
const improvements = {
520+
values: [],
521+
benchmarks: new Set(),
522+
};
523+
const all = {
524+
values: [],
525+
benchmarks: new Set(),
526+
};
527+
528+
const handleTestCase = (items, testCase) => {
529+
items.benchmarks.add(testCase.benchmark);
530+
items.values.push(testCase.percent);
531+
};
532+
533+
for (const testCase of testCases) {
534+
if (testCase.percent > 0) {
535+
handleTestCase(regressions, testCase);
536+
} else if (testCase.percent < 0) {
537+
handleTestCase(improvements, testCase);
538+
}
539+
handleTestCase(all, testCase);
540+
}
541+
542+
const computeSummary = (data) => {
543+
const values = data.values;
544+
const benchmarks = data.benchmarks;
545+
546+
const count = values.length;
547+
let range = [];
548+
if (count > 0) {
549+
range = [
550+
Math.min.apply(null, values),
551+
Math.max.apply(null, values),
552+
];
553+
}
554+
555+
const sum = values.reduce((acc, item) => acc + item, 0);
556+
const average = sum / Math.max(count, 1);
557+
558+
return {
559+
count,
560+
benchmarks: benchmarks.size,
561+
average,
562+
range,
563+
}
564+
};
565+
566+
return {
567+
improvements: computeSummary(improvements),
568+
regressions: computeSummary(regressions),
569+
all: computeSummary(all)
570+
};
571+
}
572+
473573
function unique(arr) {
474574
return arr.filter((value, idx) => arr.indexOf(value) == idx);
475575
}

0 commit comments

Comments
 (0)