feat: improve performance panel
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m54s
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m54s
This commit is contained in:
parent
66ae9e6c72
commit
bd359fbaf7
@ -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}
|
||||
|
@ -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>
|
||||
|
@ -42,6 +42,7 @@ export function createPerformanceStore(): PerformanceStore {
|
||||
});
|
||||
|
||||
data.runs.push(currentRun);
|
||||
data.runs = data.runs.slice(-100);
|
||||
currentRun = undefined;
|
||||
if (set) set(data);
|
||||
}
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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); */
|
||||
|
Loading…
Reference in New Issue
Block a user