Skip to content

Commit

Permalink
Merge pull request #1749 from Kobzol/fraction-chart
Browse files Browse the repository at this point in the history
Add a chart showing individual compilation parts to benchmark detail
  • Loading branch information
Kobzol authored Dec 8, 2023
2 parents 1ac6fb5 + 2de22b2 commit 42e9168
Show file tree
Hide file tree
Showing 11 changed files with 588 additions and 89 deletions.
80 changes: 72 additions & 8 deletions site/frontend/src/pages/compare/compile/table/benchmark-detail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,15 @@ import {GraphRenderOpts, renderPlots} from "../../../../graph/render";
import {GraphData, GraphKind, GraphsSelector} from "../../../../graph/data";
import uPlot from "uplot";
import CachegrindCmd from "../../../../components/cachegrind-cmd.vue";
import {COMPILE_DETAIL_RESOLVER} from "./detail-resolver";
import {
COMPILE_DETAIL_GRAPHS_RESOLVER,
COMPILE_DETAIL_SECTIONS_RESOLVER,
CompileDetailGraphs,
CompileDetailGraphsSelector,
CompileDetailSections,
CompileDetailSectionsSelector,
} from "./detail-resolver";
import CompileSectionsChart from "./sections-chart.vue";
import PerfettoLink from "../../../../components/perfetto-link.vue";
const props = defineProps<{
Expand Down Expand Up @@ -97,10 +105,9 @@ function drawCurrentDate(opts: GraphRenderOpts, date: Date) {
};
}
// Render both relative and absolute graphs
async function renderGraphs() {
const {start, end, date} = graphRange.value;
const selector = {
function createGraphsSelector(): CompileDetailGraphsSelector {
const {start, end} = graphRange.value;
return {
benchmark: props.testCase.benchmark,
profile: props.testCase.profile,
scenario: props.testCase.scenario,
Expand All @@ -109,7 +116,30 @@ async function renderGraphs() {
end,
kinds: ["percentrelative", "raw"] as GraphKind[],
};
const detail = await COMPILE_DETAIL_RESOLVER.loadDetail(selector);
}
async function loadGraphs(): Promise<CompileDetailGraphs> {
return await COMPILE_DETAIL_GRAPHS_RESOLVER.load(createGraphsSelector());
}
function createSectionsSelector(): CompileDetailSectionsSelector {
return {
benchmark: props.testCase.benchmark,
profile: props.testCase.profile,
scenario: props.testCase.scenario,
start: props.baseArtifact.commit,
end: props.artifact.commit,
};
}
async function loadSections(): Promise<CompileDetailSections> {
return await COMPILE_DETAIL_SECTIONS_RESOLVER.load(createSectionsSelector());
}
// Render both relative and absolute graphs
async function renderGraphs(detail: CompileDetailGraphs) {
const selector = createGraphsSelector();
const date = graphRange.value.date;
if (detail.commits.length === 0) {
return;
}
Expand Down Expand Up @@ -264,7 +294,15 @@ function changeProfileCommand(event: Event) {
profileCommand.value = target.value as ProfileCommand;
}
onMounted(() => renderGraphs());
const sectionsDetail: Ref<CompileDetailSections | null> = ref(null);
onMounted(() => {
loadGraphs().then((d) => {
renderGraphs(d);
});
loadSections().then((d) => {
sectionsDetail.value = d;
});
});
</script>

<template>
Expand Down Expand Up @@ -297,7 +335,7 @@ onMounted(() => renderGraphs());
<tr v-if="(metadata?.iterations ?? null) !== null">
<td>
Iterations
<Tooltip> How many times is the benchmark executed? </Tooltip>
<Tooltip> How many times is the benchmark executed?</Tooltip>
</td>
<td>{{ metadata.iterations }}</td>
</tr>
Expand Down Expand Up @@ -391,6 +429,32 @@ onMounted(() => renderGraphs());
<div ref="relativeChartElement"></div>
</div>
</div>
<div class="columns">
<div class="rows grow">
<div class="title bold">
Sections
<Tooltip
>Percentual duration of individual compilation sections. This is a
rough estimate that might not necessarily contain all of the
individual parts of the compilation. The sections are calculated
based on the results of self-profile queries and they are measured
based on wall-time.
</Tooltip>
</div>
<div>
<CompileSectionsChart
v-if="
(sectionsDetail?.before ?? null) !== null &&
(sectionsDetail?.after ?? null) !== null
"
:before="sectionsDetail.before"
:after="sectionsDetail.after"
/>
<span v-else-if="sectionsDetail === null">Loading…</span>
<span v-else>Not available</span>
</div>
</div>
</div>
<div class="command">
<div class="title bold">
Local profiling command<Tooltip>
Expand Down
97 changes: 72 additions & 25 deletions site/frontend/src/pages/compare/compile/table/detail-resolver.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import {GraphKind, Series} from "../../../../graph/data";
import {getJson} from "../../../../utils/requests";
import {COMPARE_COMPILE_DETAIL_DATA_URL} from "../../../../urls";
import {
COMPARE_COMPILE_DETAIL_GRAPHS_DATA_URL,
COMPARE_COMPILE_DETAIL_SECTIONS_DATA_URL,
} from "../../../../urls";
import {CachedDataLoader} from "./utils";

export interface CompileDetailSelector {
export interface CompileDetailGraphsSelector {
start: string;
end: string;
stat: string;
Expand All @@ -13,44 +17,58 @@ export interface CompileDetailSelector {
}

// Compile benchmark detail received from the server
export interface CompileDetail {
export interface CompileDetailGraphs {
commits: Array<[number, string]>;
// One Series for each GraphKind in the CompileDetailSelector
graphs: Series[];
sections_before: CompilationSections | null;
sections_after: CompilationSections | null;
}

/**
* Compile benchmark detail resolver that contains a cache of downloaded details.
* This is important for Vue components that download the benchmark detail on mount.
* Without a cache, they would download the detail each time they are destroyed
* and recreated.
*/
export class CompileBenchmarkDetailResolver {
private cache: Dict<CompileDetail> = {};
export interface CompileDetailSectionsSelector {
start: string;
end: string;
benchmark: string;
scenario: string;
profile: string;
}

public async loadDetail(
selector: CompileDetailSelector
): Promise<CompileDetail> {
const key = `${selector.benchmark};${selector.profile};${selector.scenario};${selector.start};${selector.end};${selector.stat};${selector.kinds}`;
if (!this.cache.hasOwnProperty(key)) {
this.cache[key] = await loadDetail(selector);
}
export interface CompileDetailSections {
before: CompilationSections | null;
after: CompilationSections | null;
}

export interface CompilationSection {
name: string;
value: number;
}

return this.cache[key];
}
export interface CompilationSections {
sections: CompilationSection[];
}

/**
* Compile benchmark detail resolver that contains a cache of downloaded details.
* This is important for Vue components that download the benchmark detail on mount.
* Without a cache, they would download the detail each time they are destroyed
* and recreated.
* This is essentially a global variable, but it makes the code simpler and
* since we currently don't have any unit tests, we don't really need to avoid
* global variables that much. If needed, it could be provided to Vue components
* from a parent via props or context.
*/
export const COMPILE_DETAIL_RESOLVER = new CompileBenchmarkDetailResolver();
export const COMPILE_DETAIL_GRAPHS_RESOLVER: CachedDataLoader<
CompileDetailGraphsSelector,
CompileDetailGraphs
> = new CachedDataLoader(
(key: CompileDetailGraphsSelector) =>
`${key.benchmark};${key.profile};${key.scenario};${key.start};${key.end};${key.stat};${key.kinds}`,
loadGraphsDetail
);

async function loadDetail(
selector: CompileDetailSelector
): Promise<CompileDetail> {
async function loadGraphsDetail(
selector: CompileDetailGraphsSelector
): Promise<CompileDetailGraphs> {
const params = {
start: selector.start,
end: selector.end,
Expand All @@ -60,5 +78,34 @@ async function loadDetail(
profile: selector.profile,
kinds: selector.kinds.join(","),
};
return await getJson<CompileDetail>(COMPARE_COMPILE_DETAIL_DATA_URL, params);
return await getJson<CompileDetailGraphs>(
COMPARE_COMPILE_DETAIL_GRAPHS_DATA_URL,
params
);
}

// The same thing, but for sections
export const COMPILE_DETAIL_SECTIONS_RESOLVER: CachedDataLoader<
CompileDetailSectionsSelector,
CompileDetailSections
> = new CachedDataLoader(
(key: CompileDetailGraphsSelector) =>
`${key.benchmark};${key.profile};${key.scenario};${key.start};${key.end}`,
loadSectionsDetail
);

async function loadSectionsDetail(
selector: CompileDetailSectionsSelector
): Promise<CompileDetailSections> {
const params = {
start: selector.start,
end: selector.end,
benchmark: selector.benchmark,
scenario: selector.scenario,
profile: selector.profile,
};
return await getJson<CompileDetailSections>(
COMPARE_COMPILE_DETAIL_SECTIONS_DATA_URL,
params
);
}
Loading

0 comments on commit 42e9168

Please sign in to comment.