feat: improve performance panel
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m54s

This commit is contained in:
max_richter 2024-04-25 18:40:45 +02:00
parent 66ae9e6c72
commit bd359fbaf7
10 changed files with 185 additions and 88 deletions

View File

@ -17,9 +17,6 @@
<div class="wrapper">
{#if !activeUser}
<div class="header">
<h3>Users</h3>
</div>
{#await registry.fetchUsers()}
<div>Loading...</div>
{:then users}
@ -37,15 +34,6 @@
{#await registry.fetchUser(activeUser)}
<div>Loading...</div>
{:then user}
<div class="header">
<button
on:click={() => {
$activeId = "";
}}
class="i-tabler-arrow-back"
></button>
<h3>Collections</h3>
</div>
{#each user.collections as collection}
<button
on:click={() => {
@ -59,15 +47,6 @@
<div>{error.message}</div>
{/await}
{:else if !activeNode}
<div class="header">
<button
on:click={() => {
$activeId = activeUser;
}}
class="i-tabler-arrow-back"
></button>
<h3>Nodes</h3>
</div>
{#await registry.fetchCollection(`${activeUser}/${activeCollection}`)}
<div>Loading...</div>
{:then collection}

View File

@ -2,6 +2,7 @@
import { browser } from "$app/environment";
import Monitor from "./Monitor.svelte";
import { humanizeNumber } from "$lib/helpers";
import { Checkbox } from "@nodes/ui";
type PerformanceData = {
total: Record<string, number>;
@ -10,44 +11,105 @@
export let data: PerformanceData;
export let viewer: PerformanceData;
let lastRunOnly = true;
function getPerformanceData() {
return Object.entries(data.total)
.sort((a, b) => b[1] - a[1])
.filter(([key]) => !key.startsWith("node/"));
function getTotalPerformance(onlyLast = false) {
if (onlyLast) {
return (
data.runs.at(-1).runtime[0] + viewer.runs.at(-1)["create-geometries"][0]
);
}
return data.total.runtime + viewer.total["create-geometries"];
}
function getNodePerformanceData() {
function count(input?: number | number[]) {
if (!input) return 0;
if (Array.isArray(input))
return input.reduce((acc, val) => acc + val, 0) / input.length;
return input;
}
function getCacheRatio(onlyLast = false) {
let ratio = onlyLast
? count(data.runs.at(-1)?.["cache-hit"])
: count(data.total["cache-hit"]);
return `${Math.floor(ratio * 100)}%`;
}
function getPerformanceData(onlyLast: boolean = false) {
if (onlyLast) {
return Object.entries(data.runs.at(-1))
.map(([key, value]) => [key, value[0]])
.filter(
([key]) =>
!key.startsWith("node/") &&
key !== "total" &&
!key.includes("cache"),
)
.sort((a, b) => b[1] - a[1]);
}
return Object.entries(data.total)
.sort((a, b) => b[1] - a[1])
.filter(
([key]) =>
!key.startsWith("node/") && key !== "total" && !key.includes("cache"),
);
}
function getNodePerformanceData(onlyLast: boolean = false) {
if (onlyLast) {
return Object.entries(data.runs.at(-1))
.map(([key, value]) => [key, value[0]])
.filter(([key]) => key.startsWith("node/"))
.sort((a, b) => b[1] - a[1]);
}
return Object.entries(data.total)
.filter(([key]) => key.startsWith("node/"))
.sort((a, b) => b[1] - a[1]);
}
function getViewerPerformanceData() {
function getViewerPerformanceData(onlyLast: boolean = false) {
if (onlyLast) {
return Object.entries(viewer.runs.at(-1))
.map(([key, value]) => [key, value[0]])
.filter(([key]) => key !== "total-vertices" && key !== "total-faces")
.sort((a, b) => b[1] - a[1]);
}
return Object.entries(viewer.total)
.filter(([key]) => key !== "total-vertices" && key !== "total-faces")
.sort((a, b) => b[1] - a[1]);
}
function constructPoints(key: keyof (typeof data.runs)[0]) {
return data.runs
.map((run, i) => {
return run[key][0];
})
.slice(-100);
return data.runs.map((run, i) => {
return run[key][0];
});
}
</script>
{#key data}
{#if browser}
<Monitor points={constructPoints("total")} />
<Monitor points={constructPoints("runtime")} />
{/if}
<div class="px-4">
<div class="p-4">
<div class="flex items-center gap-2">
<Checkbox id="show-total" bind:value={lastRunOnly} />
<label for="show-total">Show Average</label>
</div>
{#if data.runs.length !== 0}
<h3>General</h3>
<table>
{#each getPerformanceData() as [key, value]}
<tr>
<td>
{Math.floor(getTotalPerformance(!lastRunOnly) * 100) / 100}<span
>ms</span
>
</td>
<td>total</td>
</tr>
{#each getPerformanceData(!lastRunOnly) as [key, value]}
<tr>
<td>
{Math.floor(value * 100) / 100}<span>ms</span>
@ -58,8 +120,18 @@
</tr>
{/each}
<tr>
<td> {getCacheRatio(!lastRunOnly)} </td>
<td>cache hit</td>
</tr>
<tr>
<td>{data.runs.length}</td>
<td>Samples</td>
</tr>
<h3>Nodes</h3>
{#each getNodePerformanceData() as [key, value]}
{#each getNodePerformanceData(!lastRunOnly) as [key, value]}
<tr>
<td>
{Math.floor(value * 100) / 100}<span>ms</span>
@ -80,7 +152,7 @@
<td>{humanizeNumber(viewer.runs.at(-1)?.["total-faces"])}</td>
<td>Faces</td>
</tr>
{#each getViewerPerformanceData() as [key, value]}
{#each getViewerPerformanceData(!lastRunOnly) as [key, value]}
<tr>
<td>
{Math.floor(value * 100) / 100}<span>ms</span>

View File

@ -42,6 +42,7 @@ export function createPerformanceStore(): PerformanceStore {
});
data.runs.push(currentRun);
data.runs = data.runs.slice(-100);
currentRun = undefined;
if (set) set(data);
}

View File

@ -154,8 +154,6 @@
}
$: if (result) {
perf?.startRun();
let a = performance.now();
const inputs = parse_args(result);
let b = performance.now();
@ -194,8 +192,6 @@
perf?.addPoint("total-vertices", totalVertices);
perf?.addPoint("total-faces", totalFaces);
perf?.stopRun();
}
</script>

View File

@ -213,15 +213,22 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
let cachedValue = this.cache?.get(inputHash);
if (cachedValue !== undefined) {
log.log(`Using cached value for ${node_type.id || node.id}`);
this.perf?.addPoint("cache-hit", 1);
results[node.id] = cachedValue as Int32Array;
continue;
}
this.perf?.addPoint("cache-hit", 0);
log.group(`executing ${node_type.id || node.id}`);
log.log(`Inputs:`, inputs);
a = performance.now();
results[node.id] = node_type.execute(encoded_inputs);
b = performance.now();
if (this.cache) {
this.cache.set(inputHash, results[node.id]);
}
this.perf?.addPoint("node/" + node_type.id, b - a);
log.log("Result:", results[node.id]);
log.groupEnd();
@ -237,7 +244,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
// return the result of the parent of the output node
const res = results[outputNode.id];
this.perf?.addPoint("total", performance.now() - a0);
this.perf?.addPoint("runtime", performance.now() - a0);
this.perf?.stopRun();

View File

@ -5,17 +5,27 @@
export let id: string;
export let icon: string = "";
export let title = "";
export let classes = "";
export let hidden: boolean;
const setVisibility =
getContext<(id: string, visible: boolean) => void>("setVisibility");
$: if (typeof hidden === "boolean") {
setVisibility(id, !hidden);
}
const registerPanel =
getContext<(id: string, icon: string) => Readable<boolean>>(
"registerPanel",
);
getContext<
(id: string, icon: string, classes: string) => Readable<boolean>
>("registerPanel");
let visible = registerPanel(id, icon);
let visible = registerPanel(id, icon, classes);
console.log(id, $visible, hidden);
</script>
{#if $visible}
<div class="wrapper">
<div class="wrapper" class:hidden>
{#if title}
<header>
<h3>{title}</h3>

View File

@ -8,6 +8,8 @@
{
icon: string;
id: string;
classes: string;
visible?: boolean;
}
> = {};
@ -22,8 +24,13 @@
)
: [];
setContext("registerPanel", (id: string, icon: string) => {
panels[id] = { id, icon };
setContext("setVisibility", (id: string, visible: boolean) => {
panels[id].visible = visible;
panels = { ...panels };
});
setContext("registerPanel", (id: string, icon: string, classes: string) => {
panels[id] = { id, icon, classes };
return derived(activePanel, ($activePanel) => {
return $activePanel === id;
});
@ -50,13 +57,15 @@
<span class="absolute i-tabler-chevron-left w-6 h-6 block"></span>
</button>
{#each keys as panel (panels[panel].id)}
<button
class="tab"
class:active={panel === $activePanel}
on:click={() => setActivePanel(panel)}
>
<i class={`block w-6 h-6 ${panels[panel].icon}`} />
</button>
{#if panels[panel].visible !== false}
<button
class="tab {panels[panel].classes}"
class:active={panel === $activePanel}
on:click={() => setActivePanel(panel)}
>
<span class={`block w-6 h-6 ${panels[panel].icon}`} />
</button>
{/if}
{/each}
</div>
<div class="content">
@ -81,12 +90,6 @@
min-width: 350px;
}
h1 {
border-bottom: solid thin var(--outline);
box-sizing: border-box;
height: 70px;
}
.content {
background: var(--layer-1);
position: relative;
@ -103,35 +106,35 @@
padding: 5px;
border-radius: 0px;
background: none;
color: var(--outline);
border: none;
display: flex;
align-items: center;
border-bottom: solid thin var(--outline);
border-left: solid thin var(--outline);
background: var(--layer-0);
}
.tabs > button > span {
opacity: 0.5;
}
.tabs > button.active {
color: var(--layer-3);
background: var(--layer-1);
}
.visible .tabs > button {
border-left: none;
.tabs > button.active span {
opacity: 1;
}
.visible .tabs {
margin-left: -1px;
border-left: solid thin var(--outline);
}
.visible > .tabs button:first-child {
.visible > .tabs button:first-child > span {
transform: scaleX(-1);
}
.visible {
transform: translateX(0);
border-left: solid thin var(--outline);
background: var(--layer-0);
}
</style>

View File

@ -8,6 +8,7 @@ export const AppSettings = localStore("node-settings", {
wireframe: false,
showIndices: false,
showVertices: false,
showPerformancePanel: false,
centerCamera: true,
showStemLines: false,
amount: 5
@ -67,6 +68,11 @@ export const AppSettingTypes = {
label: "Show Indices",
value: false,
},
showPerformancePanel: {
type: "boolean",
label: "Show Performance Panel",
value: false,
},
showVertices: {
type: "boolean",
label: "Show Vertices",

View File

@ -1,10 +1,11 @@
import { MemoryRuntimeExecutor } from "./runtime-executor";
import { MemoryRuntimeExecutor, MemoryRuntimeCache } from "./runtime-executor";
import { RemoteNodeRegistry } from "./node-registry-client";
import type { Graph } from "@nodes/types";
import { createPerformanceStore } from "./performance/store";
const cache = new MemoryRuntimeCache();
const nodeRegistry = new RemoteNodeRegistry("");
const executor = new MemoryRuntimeExecutor(nodeRegistry);
const executor = new MemoryRuntimeExecutor(nodeRegistry, cache);
const performanceStore = createPerformanceStore();
executor.perf = performanceStore;

View File

@ -74,13 +74,24 @@
}
isWorking = true;
try {
let a = performance.now();
res = await workerRuntime.execute(_graph, _settings);
performanceData = await workerRuntime.getPerformanceData();
let b = performance.now();
let perfData = await workerRuntime.getPerformanceData();
let lastRun = perfData.runs?.at(-1);
if (lastRun) {
perfData.total["worker-transfer"] = b - a - lastRun.runtime[0];
lastRun["worker-transfer"] = [b - a - lastRun.runtime[0]];
}
performanceData = perfData;
isWorking = false;
} catch (error) {
console.log("errors", error);
}
viewerPerformance.stopRun();
viewerPerformance.startRun();
if (unfinished) {
let d = unfinished;
unfinished = undefined;
@ -137,21 +148,6 @@
settings={AppSettingTypes}
/>
</Panel>
<Panel id="node-store" title="Node Store" icon="i-tabler-database">
<NodeStore registry={nodeRegistry} />
</Panel>
<Panel
id="performance"
title="Performance"
icon="i-tabler-brand-speedtest"
>
{#if performanceData}
<PerformanceViewer
data={performanceData}
viewer={$viewerPerformance}
/>
{/if}
</Panel>
<Panel
id="shortcuts"
title="Keyboard Shortcuts"
@ -161,9 +157,32 @@
<Keymap {keymap} />
{/if}
</Panel>
<Panel
id="node-store"
classes="text-green-400"
title="Node Store"
icon="i-tabler-database"
>
<NodeStore registry={nodeRegistry} />
</Panel>
<Panel
id="performance"
title="Performance"
classes="text-red-400"
hidden={!$AppSettings.showPerformancePanel}
icon="i-tabler-brand-speedtest"
>
{#if performanceData}
<PerformanceViewer
data={performanceData}
viewer={$viewerPerformance}
/>
{/if}
</Panel>
<Panel
id="graph-settings"
title="Graph Settings"
classes="text-blue-400"
icon="i-tabler-brand-git"
>
{#if Object.keys(graphSettingTypes).length > 0}
@ -173,6 +192,7 @@
<Panel
id="active-node"
title="Node Settings"
classes="text-blue-400"
icon="i-tabler-adjustments"
>
<ActiveNodeSettings {manager} node={activeNode} />
@ -183,6 +203,8 @@
</Grid.Row>
</div>
<span class="font-red" />
<style>
header {
/* border-bottom: solid thin var(--outline); */