Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a chart showing individual compilation parts to benchmark detail #1749

Merged
merged 4 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading