Skip to content

Commit

Permalink
Merge pull request #49 from buggregator/issue/#33-improve-callgrph-pe…
Browse files Browse the repository at this point in the history
…rformance

Issue/#33 improve callgrph performance
  • Loading branch information
butschster authored Oct 22, 2023
2 parents 50a71a6 + 6ddcae6 commit f1b16d4
Show file tree
Hide file tree
Showing 13 changed files with 479 additions and 344 deletions.
68 changes: 0 additions & 68 deletions assets/digraph-builder.scss

This file was deleted.

1 change: 0 additions & 1 deletion assets/index.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
@import "vendor.css";
@import "digraph-builder.scss";

body {
@apply bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-50 p-0;
Expand Down
2 changes: 2 additions & 0 deletions components/ProfilerPage/ProfilerPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
<Tabs :options="{ useUrlFragment: false }" @changed="tabChange">
<Tab name="Call graph">
<ProfilerPageCallGraph
v-if="activeTab === 'Call graph'"
:event="event.payload"
@hover="setActiveEdge"
@hide="setActiveEdge"
/>
</Tab>
<Tab name="Flamechart">
<ProfilePageFlamegraph
v-if="activeTab === 'Flamechart'"
:key="activeTab"
:data-key="activeTab"
:edges="event.payload.edges"
Expand Down
180 changes: 61 additions & 119 deletions components/ProfilerPageCallGraph/ProfilerPageCallGraph.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
<template>
<div
ref="container"
class="profiler-page-call-graph"
:class="{ 'profiler-page-call-graph--fullscreen': isFullscreen }"
>
<div v-if="metricLoading" class="profiler-page-call-graph__loading-wr">
<div class="profiler-page-call-graph__loading">
<div></div>
<div></div>
<div></div>
</div>
</div>
<RenderGraph
v-if="isReadyGraph && graphKey"
:key="graphKey"
:elements="graphElements"
:height="graphHeight"
>
<template #default="{ data: { name, cost } }">
<div class="profiler-page-call-graph__board">
<h4 class="profiler-page-call-graph__board-title">
{{ name }}
</h4>

<div ref="graphviz" class="profiler-page-call-graph__graphviz"></div>
<StatBoard :cost="cost" />
</div>
</template>
</RenderGraph>

<div class="profiler-page-call-graph__toolbar">
<button title="Full screen" @click="isFullscreen = !isFullscreen">
Expand All @@ -22,22 +30,22 @@
</button>
<button
class="profiler-page-call-graph__toolbar-action"
:class="{ 'font-bold': metric === 'cpu' }"
@click="setMetric('cpu')"
:class="{ 'font-bold': metric === graphMetrics.CPU }"
@click="setMetric(graphMetrics.CPU)"
>
CPU
</button>
<button
class="profiler-page-call-graph__toolbar-action"
:class="{ 'font-bold': metric === 'pmu' }"
@click="setMetric('pmu')"
:class="{ 'font-bold': metric === graphMetrics.MEMORY_CHANGE }"
@click="setMetric(graphMetrics.MEMORY_CHANGE)"
>
Memory change
</button>
<button
class="profiler-page-call-graph__toolbar-action"
:class="{ 'font-bold': metric === 'mu' }"
@click="setMetric('mu')"
:class="{ 'font-bold': metric === graphMetrics.MEMORY }"
@click="setMetric(graphMetrics.MEMORY)"
>
Memory usage
</button>
Expand All @@ -63,19 +71,16 @@
</template>

<script lang="ts">
import { selectAll } from "d3-selection";
import { graphviz } from "d3-graphviz";
import { Graphviz } from "@hpcc-js/wasm/graphviz";
import IconSvg from "~/components/IconSvg/IconSvg.vue";
import { defineComponent, PropType } from "vue";
import { Profiler, ProfilerEdge } from "~/config/types";
import { addSlashes, DigraphBuilder } from "~/utils/digraph-builder";
import debounce from "lodash.debounce";
import { GraphTypes, Profiler } from "~/config/types";
import { calcGraphData } from "~/utils/calc-graph-data";
import RenderGraph from "~/components/RenderGraph/RenderGraph.vue";
import StatBoard from "~/components/StatBoard/StatBoard.vue";
export default defineComponent({
components: { IconSvg },
components: { StatBoard, RenderGraph, IconSvg },
props: {
event: {
type: Object as PropType<Profiler>,
Expand All @@ -86,95 +91,40 @@ export default defineComponent({
data() {
return {
isFullscreen: false,
metric: "cpu",
metricLoading: false,
metric: GraphTypes.CPU as GraphTypes,
threshold: 1,
isReadyGraph: false,
};
},
created(): void {
Graphviz.load().then(() => {
this.graph = graphviz(this.$refs.graphviz, {})
.width("100%")
.height("100%")
.fit(true);
this.renderGraph();
});
computed: {
graphElements() {
return calcGraphData(this.event.edges, this.metric, this.threshold);
},
graphKey() {
return `${this.metric}-${this.threshold}`;
},
graphMetrics() {
return GraphTypes;
},
graphHeight() {
return this.isFullscreen
? window.innerHeight
: (this.$refs.container as HTMLElement).offsetHeight;
},
},
beforeUnmount() {
this.graph.destroy();
mounted() {
// NOTE: need to show graph after parent render
this.setReadyGraph();
},
methods: {
setMetric(metric: string): void {
this.metricLoading = true;
setTimeout(() => {
this.metric = metric;
this.renderGraph();
this.metricLoading = false;
}, 0);
setMetric(metric: GraphTypes): void {
this.metric = metric;
},
setThreshold(threshold: number): void {
this.metricLoading = true;
const prevThreshold = this.threshold;
this.threshold = threshold;
return debounce(() => {
if (!threshold || prevThreshold === threshold) {
return;
}
setTimeout(() => {
this.renderGraph();
this.metricLoading = false;
}, 0);
}, 1000)();
},
findEdge(name: string): ProfilerEdge | null {
const found = Object.values(this.event.edges).filter(
(v) => addSlashes(v.callee) === name
);
if (!found || found.length === 0) {
return null;
}
return found[0] || null;
},
nodeHandler(): void {
selectAll("g.node")
.on("mouseover", (e, tag) => {
const edge = this.findEdge(tag.key);
if (!edge) {
return;
}
this.$emit("hover", {
callee: edge.callee,
cost: edge.cost,
position: {
x: e.pageX,
y: e.pageY,
},
});
})
.on("mouseout", () => {
this.$emit("hide");
});
},
renderGraph(): void {
this.graph
.renderDot(
new DigraphBuilder(this.event.edges).build(
this.metric,
this.threshold
),
this.nodeHandler
)
.resetZoom();
setReadyGraph(): void {
this.isReadyGraph = true;
},
},
});
Expand All @@ -184,11 +134,19 @@ export default defineComponent({
@import "assets/mixins";
.profiler-page-call-graph {
@apply relative flex rounded border border-gray-900 h-full;
@apply relative flex rounded border border-gray-900 min-h-min min-w-min;
}
.profiler-page-call-graph--fullscreen {
@apply rounded-none mt-0 top-0 left-0 fixed w-full h-full;
@apply rounded-none mt-0 top-0 left-0 fixed w-full h-full bg-gray-800 z-[99999];
}
.profiler-page-call-graph__board {
@apply border border-gray-600 bg-gray-800 h-full;
}
.profiler-page-call-graph__board-title {
@apply px-4 py-2 font-bold truncate text-gray-300;
}
.profiler-page-call-graph__toolbar {
Expand All @@ -214,20 +172,4 @@ export default defineComponent({
.profiler-page-call-graph__toolbar-input {
@apply border-b bg-transparent border-gray-600 text-gray-600 w-8;
}
.profiler-page-call-graph__loading-wr {
@apply absolute m-auto top-0 left-0 right-0 bottom-0 flex justify-center items-center;
}
.profiler-page-call-graph__loading {
@apply z-50;
@include loading;
}
.profiler-page-call-graph__graphviz {
@apply flex-1 justify-items-stretch items-stretch bg-white;
max-height: 100vh;
}
</style>
Loading

0 comments on commit f1b16d4

Please sign in to comment.