Skip to content

Commit dd55027

Browse files
committed
Add visualization of compilation sections
1 parent 9dd3ccb commit dd55027

File tree

3 files changed

+256
-8
lines changed

3 files changed

+256
-8
lines changed

site/frontend/src/pages/compare/compile/table/benchmark-detail.vue

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ import {GraphRenderOpts, renderPlots} from "../../../../graph/render";
1313
import {GraphData, GraphKind, GraphsSelector} from "../../../../graph/data";
1414
import uPlot from "uplot";
1515
import CachegrindCmd from "../../../../components/cachegrind-cmd.vue";
16-
import {COMPILE_DETAIL_RESOLVER} from "./detail-resolver";
16+
import {
17+
COMPILE_DETAIL_RESOLVER,
18+
CompileDetail,
19+
CompileDetailSelector,
20+
} from "./detail-resolver";
21+
import CompileSectionsChart from "./sections-chart.vue";
1722
1823
const props = defineProps<{
1924
testCase: CompileTestCase;
@@ -96,10 +101,9 @@ function drawCurrentDate(opts: GraphRenderOpts, date: Date) {
96101
};
97102
}
98103
99-
// Render both relative and absolute graphs
100-
async function renderGraphs() {
101-
const {start, end, date} = graphRange.value;
102-
const selector = {
104+
function createSelector(): CompileDetailSelector {
105+
const {start, end} = graphRange.value;
106+
return {
103107
benchmark: props.testCase.benchmark,
104108
profile: props.testCase.profile,
105109
scenario: props.testCase.scenario,
@@ -108,7 +112,16 @@ async function renderGraphs() {
108112
end,
109113
kinds: ["percentrelative", "raw"] as GraphKind[],
110114
};
111-
const detail = await COMPILE_DETAIL_RESOLVER.loadDetail(selector);
115+
}
116+
117+
async function loadDetail(): Promise<CompileDetail> {
118+
return await COMPILE_DETAIL_RESOLVER.loadDetail(createSelector());
119+
}
120+
121+
// Render both relative and absolute graphs
122+
async function renderGraphs(detail: CompileDetail) {
123+
const selector = createSelector();
124+
const date = graphRange.value.date;
112125
if (detail.commits.length === 0) {
113126
return;
114127
}
@@ -263,7 +276,13 @@ function changeProfileCommand(event: Event) {
263276
profileCommand.value = target.value as ProfileCommand;
264277
}
265278
266-
onMounted(() => renderGraphs());
279+
const detail: Ref<CompileDetail | null> = ref(null);
280+
onMounted(() => {
281+
loadDetail().then((d) => {
282+
detail.value = d;
283+
renderGraphs(d);
284+
});
285+
});
267286
</script>
268287

269288
<template>
@@ -296,7 +315,7 @@ onMounted(() => renderGraphs());
296315
<tr v-if="(metadata?.iterations ?? null) !== null">
297316
<td>
298317
Iterations
299-
<Tooltip> How many times is the benchmark executed? </Tooltip>
318+
<Tooltip> How many times is the benchmark executed?</Tooltip>
300319
</td>
301320
<td>{{ metadata.iterations }}</td>
302321
</tr>
@@ -352,6 +371,28 @@ onMounted(() => renderGraphs());
352371
</ul>
353372
</div>
354373
</div>
374+
<div class="columns">
375+
<div class="rows grow">
376+
<div class="title bold">
377+
Sections
378+
<Tooltip
379+
>Percentual duration of individual compilation sections
380+
</Tooltip>
381+
</div>
382+
<div>
383+
<CompileSectionsChart
384+
v-if="
385+
(detail?.sections_before ?? null) !== null &&
386+
(detail?.sections_after ?? null) !== null
387+
"
388+
:before="detail.sections_before"
389+
:after="detail.sections_after"
390+
/>
391+
<span v-else-if="detail === null">Loading…</span>
392+
<span v-else>Not available</span>
393+
</div>
394+
</div>
395+
</div>
355396
<div class="columns graphs">
356397
<div class="rows center-items grow">
357398
<div class="title">

site/frontend/src/pages/compare/compile/table/detail-resolver.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,22 @@ export interface CompileDetailSelector {
1212
kinds: GraphKind[];
1313
}
1414

15+
export interface CompilationSection {
16+
name: string;
17+
value: number;
18+
}
19+
20+
export interface CompilationSections {
21+
sections: CompilationSection[];
22+
}
23+
1524
// Compile benchmark detail received from the server
1625
export interface CompileDetail {
1726
commits: Array<[number, string]>;
1827
// One Series for each GraphKind in the CompileDetailSelector
1928
graphs: Series[];
29+
sections_before: CompilationSections | null;
30+
sections_after: CompilationSections | null;
2031
}
2132

2233
/**
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
<script setup lang="ts">
2+
import {CompilationSection, CompilationSections} from "./detail-resolver";
3+
import {computed, ComputedRef, Ref, ref} from "vue";
4+
5+
const props = defineProps<{
6+
before: CompilationSections;
7+
after: CompilationSections;
8+
}>();
9+
10+
const maxTotalDuration = computed(() => {
11+
const before = calculateTotalSectionsDuration(props.before);
12+
const after = calculateTotalSectionsDuration(props.after);
13+
return Math.max(before, after);
14+
});
15+
16+
function calculateTotalSectionsDuration(sections: CompilationSections): number {
17+
return sections.sections.reduce((accum, section) => accum + section.value, 0);
18+
}
19+
20+
const SECTIONS_PALETTE = [
21+
"#7768AE",
22+
"#FFCf96",
23+
"#ff8080",
24+
"#29adb2",
25+
"#3BB273",
26+
];
27+
28+
function getSectionColor(index: number): string {
29+
return SECTIONS_PALETTE[index % SECTIONS_PALETTE.length];
30+
}
31+
32+
function calculate_width(value: number): string {
33+
const fraction = value / maxTotalDuration.value;
34+
return `${(fraction * 100).toFixed(2)}%`;
35+
}
36+
37+
function formatPercent(
38+
sections: CompilationSections,
39+
sectionName: string
40+
): string {
41+
const values = sections.sections.filter((s) => s.name === sectionName);
42+
if (values.length === 0) return "??";
43+
const value = values[0].value;
44+
const total = calculateTotalSectionsDuration(sections);
45+
const percent = (value / total) * 100;
46+
return `${percent.toFixed(2)}%`;
47+
}
48+
49+
const chartRows: ComputedRef<Array<[string, CompilationSections]>> = computed(
50+
() => [
51+
["Before", props.before],
52+
["After", props.after],
53+
]
54+
);
55+
const legendItems: ComputedRef<
56+
Array<{section: CompilationSection; color: string}>
57+
> = computed(() => {
58+
const items = [];
59+
for (const section of props.before.sections) {
60+
items.push({
61+
section,
62+
color: getSectionColor(items.length),
63+
});
64+
}
65+
return items;
66+
});
67+
68+
const activeSection: Ref<string | null> = ref(null);
69+
70+
function activate(section: string) {
71+
activeSection.value = section;
72+
}
73+
function deactivate() {
74+
activeSection.value = null;
75+
}
76+
</script>
77+
78+
<template>
79+
<div class="wrapper">
80+
<div class="chart-wrapper">
81+
<div class="chart" v-for="([label, sections], rowIndex) in chartRows">
82+
<span class="label">{{ label }}</span>
83+
<div class="section-wrapper">
84+
<div
85+
v-for="(section, index) in sections.sections"
86+
:class="{section: true, active: activeSection === section.name}"
87+
@mouseenter="activate(section.name)"
88+
@mouseleave="deactivate"
89+
:style="{
90+
width: calculate_width(section.value),
91+
backgroundColor: getSectionColor(index),
92+
}"
93+
>
94+
<div
95+
class="description"
96+
v-if="rowIndex == 1 && activeSection === section.name"
97+
>
98+
<div>
99+
<b>{{ section.name }}</b>
100+
</div>
101+
<div>
102+
{{ formatPercent(props.before, section.name) }} ->
103+
{{ formatPercent(props.after, section.name) }}
104+
</div>
105+
</div>
106+
</div>
107+
</div>
108+
</div>
109+
</div>
110+
<div class="legend">
111+
<div
112+
class="item"
113+
v-for="item in legendItems"
114+
@mouseenter="activate(item.section.name)"
115+
@mouseleave="deactivate"
116+
>
117+
<div
118+
:class="{color: true, active: activeSection === item.section.name}"
119+
:style="{backgroundColor: item.color}"
120+
></div>
121+
<div class="name">{{ item.section.name }}</div>
122+
</div>
123+
</div>
124+
</div>
125+
</template>
126+
127+
<style scoped lang="scss">
128+
.wrapper {
129+
display: flex;
130+
}
131+
.chart {
132+
display: flex;
133+
justify-content: flex-end;
134+
width: 600px;
135+
136+
&:first-child {
137+
margin-bottom: 10px;
138+
}
139+
140+
.label {
141+
width: 55px;
142+
margin-right: 5px;
143+
align-self: center;
144+
}
145+
146+
.section-wrapper {
147+
width: calc(100% - 60px);
148+
display: flex;
149+
flex-direction: row;
150+
border-right: 1px dashed #333333;
151+
152+
.section {
153+
height: 30px;
154+
position: relative;
155+
}
156+
}
157+
158+
.description {
159+
position: absolute;
160+
top: 35px;
161+
width: max-content;
162+
z-index: 99;
163+
padding: 10px;
164+
background-color: white;
165+
border: 2px solid black;
166+
}
167+
}
168+
169+
.active {
170+
box-shadow: inset 0 0 1px 2px #000;
171+
}
172+
173+
.section:first-child {
174+
border-radius: 5px 0 0 5px;
175+
}
176+
177+
.section:last-child {
178+
border-radius: 0 5px 5px 0;
179+
}
180+
.legend {
181+
margin-left: 40px;
182+
183+
.item {
184+
display: flex;
185+
margin-bottom: 5px;
186+
187+
.color {
188+
width: 15px;
189+
height: 15px;
190+
}
191+
.name {
192+
margin-left: 5px;
193+
}
194+
}
195+
}
196+
</style>

0 commit comments

Comments
 (0)