diff --git a/app/src/lib/node-store/NodeStore.svelte b/app/src/lib/node-store/NodeStore.svelte index 6190726..2620f73 100644 --- a/app/src/lib/node-store/NodeStore.svelte +++ b/app/src/lib/node-store/NodeStore.svelte @@ -17,9 +17,6 @@
{#if !activeUser} -
-

Users

-
{#await registry.fetchUsers()}
Loading...
{:then users} @@ -37,15 +34,6 @@ {#await registry.fetchUser(activeUser)}
Loading...
{:then user} -
- -

Collections

-
{#each user.collections as collection} -

Nodes

-
{#await registry.fetchCollection(`${activeUser}/${activeCollection}`)}
Loading...
{:then collection} diff --git a/app/src/lib/performance/PerformanceViewer.svelte b/app/src/lib/performance/PerformanceViewer.svelte index a6cda26..29364d0 100644 --- a/app/src/lib/performance/PerformanceViewer.svelte +++ b/app/src/lib/performance/PerformanceViewer.svelte @@ -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; @@ -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]; + }); } {#key data} {#if browser} - + {/if} -
+
+
+ + +
{#if data.runs.length !== 0}

General

- {#each getPerformanceData() as [key, value]} + + + + + {#each getPerformanceData(!lastRunOnly) as [key, value]} {/each} + + + + + + + + + +

Nodes

- {#each getNodePerformanceData() as [key, value]} + {#each getNodePerformanceData(!lastRunOnly) as [key, value]} - {#each getViewerPerformanceData() as [key, value]} + {#each getViewerPerformanceData(!lastRunOnly) as [key, value]}
+ {Math.floor(getTotalPerformance(!lastRunOnly) * 100) / 100}ms + total
{Math.floor(value * 100) / 100}ms @@ -58,8 +120,18 @@
{getCacheRatio(!lastRunOnly)} cache hit
{data.runs.length}Samples
{Math.floor(value * 100) / 100}ms @@ -80,7 +152,7 @@ {humanizeNumber(viewer.runs.at(-1)?.["total-faces"])} Faces
{Math.floor(value * 100) / 100}ms diff --git a/app/src/lib/performance/store.ts b/app/src/lib/performance/store.ts index 75a66fd..6dbbe48 100644 --- a/app/src/lib/performance/store.ts +++ b/app/src/lib/performance/store.ts @@ -42,6 +42,7 @@ export function createPerformanceStore(): PerformanceStore { }); data.runs.push(currentRun); + data.runs = data.runs.slice(-100); currentRun = undefined; if (set) set(data); } diff --git a/app/src/lib/result-viewer/Viewer.svelte b/app/src/lib/result-viewer/Viewer.svelte index e2ab796..bb7e603 100644 --- a/app/src/lib/result-viewer/Viewer.svelte +++ b/app/src/lib/result-viewer/Viewer.svelte @@ -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(); } diff --git a/app/src/lib/runtime-executor.ts b/app/src/lib/runtime-executor.ts index 1fb847d..7a33af7 100644 --- a/app/src/lib/runtime-executor.ts +++ b/app/src/lib/runtime-executor.ts @@ -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(); diff --git a/app/src/lib/settings/Panel.svelte b/app/src/lib/settings/Panel.svelte index 5f6e198..271ea34 100644 --- a/app/src/lib/settings/Panel.svelte +++ b/app/src/lib/settings/Panel.svelte @@ -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>( - "registerPanel", - ); + getContext< + (id: string, icon: string, classes: string) => Readable + >("registerPanel"); - let visible = registerPanel(id, icon); + let visible = registerPanel(id, icon, classes); + console.log(id, $visible, hidden); {#if $visible} -
+
{#if title}

{title}

diff --git a/app/src/lib/settings/Settings.svelte b/app/src/lib/settings/Settings.svelte index b8dbae7..cffaeff 100644 --- a/app/src/lib/settings/Settings.svelte +++ b/app/src/lib/settings/Settings.svelte @@ -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 @@ {#each keys as panel (panels[panel].id)} - + {#if panels[panel].visible !== false} + + {/if} {/each}
@@ -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); } diff --git a/app/src/lib/settings/app-settings.ts b/app/src/lib/settings/app-settings.ts index e25cc72..c7c92dc 100644 --- a/app/src/lib/settings/app-settings.ts +++ b/app/src/lib/settings/app-settings.ts @@ -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", diff --git a/app/src/lib/worker-runtime-executor-backend.ts b/app/src/lib/worker-runtime-executor-backend.ts index 6308687..a5d646e 100644 --- a/app/src/lib/worker-runtime-executor-backend.ts +++ b/app/src/lib/worker-runtime-executor-backend.ts @@ -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; diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte index e6ad7f1..7666101 100644 --- a/app/src/routes/+page.svelte +++ b/app/src/routes/+page.svelte @@ -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} /> - - - - - {#if performanceData} - - {/if} - {/if} + + + + {#if Object.keys(graphSettingTypes).length > 0} @@ -173,6 +192,7 @@ @@ -183,6 +203,8 @@
+ +