feat: refactor performance collection
Some checks failed
Deploy to GitHub Pages / build_site (push) Has been cancelled
Some checks failed
Deploy to GitHub Pages / build_site (push) Has been cancelled
This commit is contained in:
parent
6eaecef35d
commit
2f0022f912
@ -1,9 +1,32 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let points: number[];
|
export let points: number[];
|
||||||
|
|
||||||
$: max = Math.max(...points);
|
export let type = "ms";
|
||||||
$: min = Math.min(...points);
|
export let title = "Performance";
|
||||||
|
export let max: number | undefined = undefined;
|
||||||
|
export let min: number | undefined = undefined;
|
||||||
|
|
||||||
|
function getMax(m?: number) {
|
||||||
|
if (type === "%") {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m !== undefined) {
|
||||||
|
if (m < 1) {
|
||||||
|
return Math.floor(m * 100) / 100;
|
||||||
|
}
|
||||||
|
if (m < 10) {
|
||||||
|
return Math.floor(m * 10) / 10;
|
||||||
|
}
|
||||||
|
return Math.floor(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
function constructPath() {
|
function constructPath() {
|
||||||
|
max = max !== undefined ? max : Math.max(...points);
|
||||||
|
min = min !== undefined ? min : Math.min(...points);
|
||||||
return points
|
return points
|
||||||
.map((point, i) => {
|
.map((point, i) => {
|
||||||
const x = (i / (points.length - 1)) * 100;
|
const x = (i / (points.length - 1)) * 100;
|
||||||
@ -15,11 +38,13 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<p>Runtime Execution</p>
|
<p>{title}</p>
|
||||||
<span class="min">{min}ms</span>
|
<span class="min">{Math.floor(min || 0)}{type}</span>
|
||||||
<span class="max">{max}ms</span>
|
<span class="max">{getMax(max)}{type}</span>
|
||||||
<svg preserveAspectRatio="none" viewBox="0 0 100 100">
|
<svg preserveAspectRatio="none" viewBox="0 0 100 100">
|
||||||
<polyline vector-effect="non-scaling-stroke" points={constructPath()} />
|
{#key points}
|
||||||
|
<polyline vector-effect="non-scaling-stroke" points={constructPath()} />
|
||||||
|
{/key}
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,168 +1,265 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { browser } from "$app/environment";
|
|
||||||
import Monitor from "./Monitor.svelte";
|
import Monitor from "./Monitor.svelte";
|
||||||
import { humanizeNumber } from "$lib/helpers";
|
import { humanizeNumber } from "$lib/helpers";
|
||||||
import { Checkbox } from "@nodes/ui";
|
import { Checkbox } from "@nodes/ui";
|
||||||
|
import localStore from "$lib/helpers/localStore";
|
||||||
type PerformanceData = {
|
import { type PerformanceData } from "./store";
|
||||||
total: Record<string, number>;
|
|
||||||
runs: Record<string, number[]>[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export let data: PerformanceData;
|
export let data: PerformanceData;
|
||||||
export let viewer: PerformanceData;
|
|
||||||
let lastRunOnly = true;
|
let activeType = localStore<string>("nodes.performance.active-type", "total");
|
||||||
|
let showAverage = true;
|
||||||
|
|
||||||
|
function getAverage(key: string) {
|
||||||
|
return (
|
||||||
|
data
|
||||||
|
.map((run) => run[key]?.[0])
|
||||||
|
.filter((v) => v !== undefined)
|
||||||
|
.reduce((acc, run) => acc + run, 0) / data.length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function round(v: number) {
|
||||||
|
if (v < 1) {
|
||||||
|
return Math.floor(v * 100) / 100;
|
||||||
|
}
|
||||||
|
if (v < 10) {
|
||||||
|
return Math.floor(v * 10) / 10;
|
||||||
|
}
|
||||||
|
return Math.floor(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAverages() {
|
||||||
|
let lastRun = data.at(-1);
|
||||||
|
if (!lastRun) return {};
|
||||||
|
return Object.keys(lastRun).reduce(
|
||||||
|
(acc, key) => {
|
||||||
|
acc[key] = getAverage(key);
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, number>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLast(key: string) {
|
||||||
|
return data.at(-1)?.[key][0] || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLasts() {
|
||||||
|
return data.at(-1) || {};
|
||||||
|
}
|
||||||
|
|
||||||
function getTotalPerformance(onlyLast = false) {
|
function getTotalPerformance(onlyLast = false) {
|
||||||
if (onlyLast) {
|
if (onlyLast) {
|
||||||
return (
|
return (
|
||||||
data.runs.at(-1).runtime[0] + viewer.runs.at(-1)["create-geometries"][0]
|
getLast("runtime") +
|
||||||
|
getLast("create-geometries") +
|
||||||
|
getLast("worker-transfer")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return data.total.runtime + viewer.total["create-geometries"];
|
return (
|
||||||
}
|
getAverage("runtime") +
|
||||||
|
getAverage("create-geometries") +
|
||||||
function count(input?: number | number[]) {
|
getAverage("worker-transfer")
|
||||||
if (!input) return 0;
|
);
|
||||||
if (Array.isArray(input))
|
|
||||||
return input.reduce((acc, val) => acc + val, 0) / input.length;
|
|
||||||
return input;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCacheRatio(onlyLast = false) {
|
function getCacheRatio(onlyLast = false) {
|
||||||
let ratio = onlyLast
|
let ratio = onlyLast ? getLast("cache-hit") : getAverage("cache-hit");
|
||||||
? count(data.runs.at(-1)?.["cache-hit"])
|
|
||||||
: count(data.total["cache-hit"]);
|
|
||||||
|
|
||||||
return `${Math.floor(ratio * 100)}%`;
|
return Math.floor(ratio * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const viewerKeys = [
|
||||||
|
"total-vertices",
|
||||||
|
"total-faces",
|
||||||
|
"create-geometries",
|
||||||
|
"split-result",
|
||||||
|
];
|
||||||
|
|
||||||
function getPerformanceData(onlyLast: boolean = false) {
|
function getPerformanceData(onlyLast: boolean = false) {
|
||||||
if (onlyLast) {
|
let data = onlyLast ? getLasts() : getAverages();
|
||||||
return Object.entries(data.runs.at(-1))
|
|
||||||
.map(([key, value]) => [key, value[0]])
|
return Object.entries(data)
|
||||||
.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(
|
.filter(
|
||||||
([key]) =>
|
([key]) =>
|
||||||
!key.startsWith("node/") && key !== "total" && !key.includes("cache"),
|
!key.startsWith("node/") &&
|
||||||
);
|
key !== "total" &&
|
||||||
|
!key.includes("cache") &&
|
||||||
|
!viewerKeys.includes(key),
|
||||||
|
)
|
||||||
|
.sort((a, b) => b[1] - a[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNodePerformanceData(onlyLast: boolean = false) {
|
function getNodePerformanceData(onlyLast: boolean = false) {
|
||||||
if (onlyLast) {
|
let data = onlyLast ? getLasts() : getAverages();
|
||||||
return Object.entries(data.runs.at(-1))
|
|
||||||
.map(([key, value]) => [key, value[0]])
|
return Object.entries(data)
|
||||||
.filter(([key]) => key.startsWith("node/"))
|
|
||||||
.sort((a, b) => b[1] - a[1]);
|
|
||||||
}
|
|
||||||
return Object.entries(data.total)
|
|
||||||
.filter(([key]) => key.startsWith("node/"))
|
.filter(([key]) => key.startsWith("node/"))
|
||||||
.sort((a, b) => b[1] - a[1]);
|
.sort((a, b) => b[1] - a[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getViewerPerformanceData(onlyLast: boolean = false) {
|
function getViewerPerformanceData(onlyLast: boolean = false) {
|
||||||
if (onlyLast) {
|
let data = onlyLast ? getLasts() : getAverages();
|
||||||
return Object.entries(viewer.runs.at(-1))
|
return Object.entries(data)
|
||||||
.map(([key, value]) => [key, value[0]])
|
.filter(
|
||||||
.filter(([key]) => key !== "total-vertices" && key !== "total-faces")
|
([key]) =>
|
||||||
.sort((a, b) => b[1] - a[1]);
|
key !== "total-vertices" &&
|
||||||
}
|
key !== "total-faces" &&
|
||||||
return Object.entries(viewer.total)
|
viewerKeys.includes(key),
|
||||||
.filter(([key]) => key !== "total-vertices" && key !== "total-faces")
|
)
|
||||||
.sort((a, b) => b[1] - a[1]);
|
.sort((a, b) => b[1] - a[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function constructPoints(key: keyof (typeof data.runs)[0]) {
|
function getTotalPoints() {
|
||||||
return data.runs.map((run, i) => {
|
if (showAverage) {
|
||||||
return run[key][0];
|
return data.map((run) => {
|
||||||
|
return (
|
||||||
|
run["runtime"].reduce((acc, v) => acc + v, 0) +
|
||||||
|
run["create-geometries"].reduce((acc, v) => acc + v, 0) +
|
||||||
|
run["worker-transfer"].reduce((acc, v) => acc + v, 0)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.map((run) => {
|
||||||
|
return (
|
||||||
|
run["runtime"][0] +
|
||||||
|
run["create-geometries"][0] +
|
||||||
|
run["worker-transfer"][0]
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function constructPoints(key: string) {
|
||||||
|
if (key === "total") {
|
||||||
|
return getTotalPoints();
|
||||||
|
}
|
||||||
|
return data.map((run) => {
|
||||||
|
if (key in run) {
|
||||||
|
if (showAverage) {
|
||||||
|
return run[key].reduce((acc, v) => acc + v, 0) / run[key].length;
|
||||||
|
} else {
|
||||||
|
return run[key][0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTitle(t: string) {
|
||||||
|
if (t.includes("/")) {
|
||||||
|
return `Node ${t.split("/").slice(-1).join("/")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
.split("-")
|
||||||
|
.map((v) => v[0].toUpperCase() + v.slice(1))
|
||||||
|
.join(" ");
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#key data}
|
{#key $activeType && data}
|
||||||
{#if browser}
|
{#if $activeType === "cache-hit"}
|
||||||
<Monitor points={constructPoints("runtime")} />
|
<Monitor
|
||||||
|
title="Cache Hits"
|
||||||
|
points={constructPoints($activeType)}
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
type="%"
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<Monitor
|
||||||
|
title={getTitle($activeType)}
|
||||||
|
points={constructPoints($activeType)}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Checkbox id="show-total" bind:value={lastRunOnly} />
|
<Checkbox id="show-total" bind:value={showAverage} />
|
||||||
<label for="show-total">Show Average</label>
|
<label for="show-total">Show Average</label>
|
||||||
</div>
|
</div>
|
||||||
{#if data.runs.length !== 0}
|
{#if data.length !== 0}
|
||||||
<h3>General</h3>
|
<h3>General</h3>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{Math.floor(getTotalPerformance(!lastRunOnly) * 100) / 100}<span
|
{round(getTotalPerformance(!showAverage))}<span>ms</span>
|
||||||
>ms</span
|
</td>
|
||||||
|
<td
|
||||||
|
class:active={$activeType === "total"}
|
||||||
|
on:click={() => ($activeType = "total")}
|
||||||
|
>
|
||||||
|
total<span
|
||||||
|
>({Math.floor(1000 / getTotalPerformance(showAverage))}fps)</span
|
||||||
>
|
>
|
||||||
</td>
|
</td>
|
||||||
<td>total</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{#each getPerformanceData(!lastRunOnly) as [key, value]}
|
{#each getPerformanceData(!showAverage) as [key, value]}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{Math.floor(value * 100) / 100}<span>ms</span>
|
{round(value)}<span>ms</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td
|
||||||
|
class:active={$activeType === key}
|
||||||
|
on:click={() => ($activeType = key)}
|
||||||
|
>
|
||||||
{key}
|
{key}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td> {getCacheRatio(!lastRunOnly)} </td>
|
<td>{data.length}</td>
|
||||||
<td>cache hit</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>{data.runs.length}</td>
|
|
||||||
<td>Samples</td>
|
<td>Samples</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<h3>Nodes</h3>
|
<h3>Nodes</h3>
|
||||||
{#each getNodePerformanceData(!lastRunOnly) as [key, value]}
|
<tr>
|
||||||
|
<td> {getCacheRatio(!showAverage)}<span>%</span> </td>
|
||||||
|
<td
|
||||||
|
class:active={$activeType === "cache-hit"}
|
||||||
|
on:click={() => ($activeType = "cache-hit")}>cache hits</td
|
||||||
|
>
|
||||||
|
</tr>
|
||||||
|
{#each getNodePerformanceData(!showAverage) as [key, value]}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{Math.floor(value * 100) / 100}<span>ms</span>
|
{round(value)}<span>ms</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
|
||||||
|
<td
|
||||||
|
class:active={$activeType === key}
|
||||||
|
on:click={() => ($activeType = key)}
|
||||||
|
>
|
||||||
{key.split("/").slice(-1).join("/")}
|
{key.split("/").slice(-1).join("/")}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
{#if viewer.runs.length}
|
<h3>Viewer</h3>
|
||||||
<h3>Viewer</h3>
|
<tr>
|
||||||
|
<td>{humanizeNumber(getLast("total-vertices"))}</td>
|
||||||
|
<td>Vertices</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{humanizeNumber(getLast("total-faces"))}</td>
|
||||||
|
<td>Faces</td>
|
||||||
|
</tr>
|
||||||
|
{#each getViewerPerformanceData(!showAverage) as [key, value]}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{humanizeNumber(viewer.runs.at(-1)?.["total-vertices"])}</td>
|
<td>
|
||||||
<td>Vertices</td>
|
{round(value)}<span>ms</span>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
class:active={$activeType === key}
|
||||||
|
on:click={() => ($activeType = key)}
|
||||||
|
>
|
||||||
|
{key.split("/").slice(-1).join("/")}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
{/each}
|
||||||
<td>{humanizeNumber(viewer.runs.at(-1)?.["total-faces"])}</td>
|
|
||||||
<td>Faces</td>
|
|
||||||
</tr>
|
|
||||||
{#each getViewerPerformanceData(!lastRunOnly) as [key, value]}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{Math.floor(value * 100) / 100}<span>ms</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{key.split("/").slice(-1).join("/")}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
</table>
|
</table>
|
||||||
{:else}
|
{:else}
|
||||||
<p>No runs available</p>
|
<p>No runs available</p>
|
||||||
@ -185,6 +282,9 @@
|
|||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
padding-block: 5px;
|
padding-block: 5px;
|
||||||
}
|
}
|
||||||
|
td.active {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
tr > td:nth-child(1) {
|
tr > td:nth-child(1) {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,24 @@
|
|||||||
import { readable, type Readable } from "svelte/store";
|
import { readable, type Readable } from "svelte/store";
|
||||||
|
|
||||||
export type PerformanceData = {
|
export type PerformanceData = Record<string, number[]>[];
|
||||||
total: Record<string, number>;
|
|
||||||
runs: Record<string, number[]>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PerformanceStore extends Readable<PerformanceData> {
|
export interface PerformanceStore extends Readable<PerformanceData> {
|
||||||
startRun(): void;
|
startRun(): void;
|
||||||
stopRun(): void;
|
stopRun(): void;
|
||||||
addPoint(name: string, value?: number): void;
|
addPoint(name: string, value?: number): void;
|
||||||
|
mergeData(data: PerformanceData[number]): void;
|
||||||
get: () => PerformanceData;
|
get: () => PerformanceData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createPerformanceStore(): PerformanceStore {
|
export function createPerformanceStore(): PerformanceStore {
|
||||||
|
|
||||||
let data: PerformanceData = { total: {}, runs: [] };
|
let data: PerformanceData = [];
|
||||||
|
|
||||||
let currentRun: Record<string, number[]> | undefined;
|
let currentRun: Record<string, number[]> | undefined;
|
||||||
|
|
||||||
let set: (v: PerformanceData) => void;
|
let set: (v: PerformanceData) => void;
|
||||||
|
|
||||||
const { subscribe } = readable<PerformanceData>({ total: {}, runs: [] }, (_set) => {
|
const { subscribe } = readable<PerformanceData>([], (_set) => {
|
||||||
set = _set;
|
set = _set;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -30,19 +28,8 @@ export function createPerformanceStore(): PerformanceStore {
|
|||||||
|
|
||||||
function stopRun() {
|
function stopRun() {
|
||||||
if (currentRun) {
|
if (currentRun) {
|
||||||
// Calculate total
|
data.push(currentRun);
|
||||||
Object.keys(currentRun).forEach((name) => {
|
data = data.slice(-100);
|
||||||
if (!currentRun?.[name]?.length) return;
|
|
||||||
let runTotal = currentRun[name].reduce((a, b) => a + b, 0) / currentRun[name].length;
|
|
||||||
if (!data.total[name]) {
|
|
||||||
data.total[name] = runTotal;
|
|
||||||
} else {
|
|
||||||
data.total[name] = (data.total[name] + runTotal) / 2;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
data.runs.push(currentRun);
|
|
||||||
data.runs = data.runs.slice(-100);
|
|
||||||
currentRun = undefined;
|
currentRun = undefined;
|
||||||
if (set) set(data);
|
if (set) set(data);
|
||||||
}
|
}
|
||||||
@ -58,11 +45,26 @@ export function createPerformanceStore(): PerformanceStore {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mergeData(newData: PerformanceData[number]) {
|
||||||
|
|
||||||
|
let r = currentRun;
|
||||||
|
if (!r) return;
|
||||||
|
|
||||||
|
Object.keys(newData).forEach((name) => {
|
||||||
|
if (name in r) {
|
||||||
|
r[name].push(...newData[name]);
|
||||||
|
} else {
|
||||||
|
r[name] = newData[name];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe,
|
subscribe,
|
||||||
startRun,
|
startRun,
|
||||||
stopRun,
|
stopRun,
|
||||||
addPoint,
|
addPoint,
|
||||||
|
mergeData,
|
||||||
get
|
get
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,6 @@
|
|||||||
let geometries: BufferGeometry[] = [];
|
let geometries: BufferGeometry[] = [];
|
||||||
let lines: Vector3[][] = [];
|
let lines: Vector3[][] = [];
|
||||||
|
|
||||||
let totalVertices = 0;
|
|
||||||
let totalFaces = 0;
|
|
||||||
|
|
||||||
function fastArrayHash(arr: ArrayBuffer) {
|
function fastArrayHash(arr: ArrayBuffer) {
|
||||||
let ints = new Uint8Array(arr);
|
let ints = new Uint8Array(arr);
|
||||||
|
|
||||||
@ -41,14 +38,11 @@
|
|||||||
geometry = new BufferGeometry(),
|
geometry = new BufferGeometry(),
|
||||||
): BufferGeometry {
|
): BufferGeometry {
|
||||||
// Extract data from the encoded array
|
// Extract data from the encoded array
|
||||||
let index = 0;
|
let index = 1;
|
||||||
const geometryType = encodedData[index++];
|
// const geometryType = encodedData[index++];
|
||||||
const vertexCount = encodedData[index++];
|
const vertexCount = encodedData[index++];
|
||||||
const faceCount = encodedData[index++];
|
const faceCount = encodedData[index++];
|
||||||
|
|
||||||
totalVertices += vertexCount;
|
|
||||||
totalFaces += faceCount;
|
|
||||||
|
|
||||||
// Indices
|
// Indices
|
||||||
const indicesEnd = index + faceCount * 3;
|
const indicesEnd = index + faceCount * 3;
|
||||||
const indices = encodedData.subarray(index, indicesEnd);
|
const indices = encodedData.subarray(index, indicesEnd);
|
||||||
@ -62,10 +56,24 @@
|
|||||||
);
|
);
|
||||||
index = index + vertexCount * 3;
|
index = index + vertexCount * 3;
|
||||||
let hash = fastArrayHash(vertices);
|
let hash = fastArrayHash(vertices);
|
||||||
|
let posAttribute = geometry.getAttribute(
|
||||||
|
"position",
|
||||||
|
) as BufferAttribute | null;
|
||||||
|
|
||||||
if (geometry.userData?.hash === hash) {
|
if (geometry.userData?.hash === hash) {
|
||||||
return geometry;
|
return geometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (posAttribute && posAttribute.count === vertexCount) {
|
||||||
|
posAttribute.set(vertices, 0);
|
||||||
|
posAttribute.needsUpdate = true;
|
||||||
|
} else {
|
||||||
|
geometry.setAttribute(
|
||||||
|
"position",
|
||||||
|
new Float32BufferAttribute(vertices, 3),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const normals = new Float32Array(
|
const normals = new Float32Array(
|
||||||
encodedData.buffer,
|
encodedData.buffer,
|
||||||
index * 4,
|
index * 4,
|
||||||
@ -81,20 +89,6 @@
|
|||||||
geometry.setIndex([...indices]);
|
geometry.setIndex([...indices]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let posAttribute = geometry.getAttribute(
|
|
||||||
"position",
|
|
||||||
) as BufferAttribute | null;
|
|
||||||
|
|
||||||
if (posAttribute && posAttribute.count === vertexCount) {
|
|
||||||
posAttribute.set(vertices, 0);
|
|
||||||
posAttribute.needsUpdate = true;
|
|
||||||
} else {
|
|
||||||
geometry.setAttribute(
|
|
||||||
"position",
|
|
||||||
new Float32BufferAttribute(vertices, 3),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalsAttribute = geometry.getAttribute(
|
const normalsAttribute = geometry.getAttribute(
|
||||||
"normal",
|
"normal",
|
||||||
) as BufferAttribute | null;
|
) as BufferAttribute | null;
|
||||||
@ -172,15 +166,14 @@
|
|||||||
return positions;
|
return positions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const update = function updateGeometries(result: Int32Array) {
|
export let result: Int32Array;
|
||||||
|
$: result && updateGeometries();
|
||||||
|
function updateGeometries() {
|
||||||
let a = performance.now();
|
let a = performance.now();
|
||||||
const inputs = parse_args(result);
|
const inputs = parse_args(result);
|
||||||
let b = performance.now();
|
let b = performance.now();
|
||||||
perf?.addPoint("split-result", b - a);
|
perf?.addPoint("split-result", b - a);
|
||||||
|
|
||||||
totalVertices = 0;
|
|
||||||
totalFaces = 0;
|
|
||||||
|
|
||||||
if ($AppSettings.showStemLines) {
|
if ($AppSettings.showStemLines) {
|
||||||
a = performance.now();
|
a = performance.now();
|
||||||
lines = inputs
|
lines = inputs
|
||||||
@ -194,11 +187,17 @@
|
|||||||
perf?.addPoint("create-lines", b - a);
|
perf?.addPoint("create-lines", b - a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let totalVertices = 0;
|
||||||
|
let totalFaces = 0;
|
||||||
|
|
||||||
a = performance.now();
|
a = performance.now();
|
||||||
geometries = inputs
|
geometries = inputs
|
||||||
.map((input, i) => {
|
.map((input, i) => {
|
||||||
if (input[0] === 1) {
|
if (input[0] === 1) {
|
||||||
return createGeometryFromEncodedData(input, geometries[i]);
|
let geo = createGeometryFromEncodedData(input);
|
||||||
|
totalVertices += geo.userData.vertexCount;
|
||||||
|
totalFaces += geo.userData.faceCount;
|
||||||
|
return geo;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(Boolean) as BufferGeometry[];
|
.filter(Boolean) as BufferGeometry[];
|
||||||
@ -206,7 +205,7 @@
|
|||||||
perf?.addPoint("create-geometries", b - a);
|
perf?.addPoint("create-geometries", b - a);
|
||||||
perf?.addPoint("total-vertices", totalVertices);
|
perf?.addPoint("total-vertices", totalVertices);
|
||||||
perf?.addPoint("total-faces", totalFaces);
|
perf?.addPoint("total-faces", totalFaces);
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Canvas>
|
<Canvas>
|
||||||
|
@ -45,7 +45,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
|||||||
|
|
||||||
perf?: PerformanceStore;
|
perf?: PerformanceStore;
|
||||||
|
|
||||||
constructor(private registry: NodeRegistry, private cache?: RuntimeCache) { }
|
constructor(private registry: NodeRegistry, private cache?: RuntimeCache<Int32Array>) { }
|
||||||
|
|
||||||
private async getNodeDefinitions(graph: Graph) {
|
private async getNodeDefinitions(graph: Graph) {
|
||||||
|
|
||||||
@ -215,6 +215,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
|||||||
let inputHash = `node-${node.id}-${fastHashArrayBuffer(encoded_inputs)}`;
|
let inputHash = `node-${node.id}-${fastHashArrayBuffer(encoded_inputs)}`;
|
||||||
b = performance.now();
|
b = performance.now();
|
||||||
this.perf?.addPoint("hash-inputs", b - a);
|
this.perf?.addPoint("hash-inputs", b - a);
|
||||||
|
|
||||||
let cachedValue = this.cache?.get(inputHash);
|
let cachedValue = this.cache?.get(inputHash);
|
||||||
if (cachedValue !== undefined) {
|
if (cachedValue !== undefined) {
|
||||||
log.log(`Using cached value for ${node_type.id || node.id}`);
|
log.log(`Using cached value for ${node_type.id || node.id}`);
|
||||||
@ -253,6 +254,10 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
|||||||
|
|
||||||
this.perf?.stopRun();
|
this.perf?.stopRun();
|
||||||
|
|
||||||
|
if (this.cache) {
|
||||||
|
this.cache.size = sortedNodes.length * 2;
|
||||||
|
}
|
||||||
|
|
||||||
return res as unknown as Int32Array;
|
return res as unknown as Int32Array;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -261,15 +266,18 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
|||||||
|
|
||||||
export class MemoryRuntimeCache implements RuntimeCache {
|
export class MemoryRuntimeCache implements RuntimeCache {
|
||||||
|
|
||||||
private cache: Record<string, unknown> = {};
|
private cache: [string, unknown][] = [];
|
||||||
|
size = 50;
|
||||||
|
|
||||||
get<T>(key: string): T | undefined {
|
get<T>(key: string): T | undefined {
|
||||||
return this.cache[key] as T;
|
return this.cache.find(([k]) => k === key)?.[1] as T;
|
||||||
}
|
}
|
||||||
set<T>(key: string, value: T): void {
|
set<T>(key: string, value: T): void {
|
||||||
this.cache[key] = value;
|
this.cache.push([key, value]);
|
||||||
|
this.cache = this.cache.slice(-this.size);
|
||||||
}
|
}
|
||||||
clear(): void {
|
clear(): void {
|
||||||
this.cache = {};
|
this.cache = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,18 +20,15 @@
|
|||||||
import GraphSettings from "$lib/settings/panels/GraphSettings.svelte";
|
import GraphSettings from "$lib/settings/panels/GraphSettings.svelte";
|
||||||
import NestedSettings from "$lib/settings/panels/NestedSettings.svelte";
|
import NestedSettings from "$lib/settings/panels/NestedSettings.svelte";
|
||||||
import { createPerformanceStore } from "$lib/performance";
|
import { createPerformanceStore } from "$lib/performance";
|
||||||
import { type PerformanceData } from "$lib/performance/store";
|
|
||||||
|
|
||||||
const nodeRegistry = new RemoteNodeRegistry("");
|
const nodeRegistry = new RemoteNodeRegistry("");
|
||||||
const workerRuntime = new WorkerRuntimeExecutor();
|
const workerRuntime = new WorkerRuntimeExecutor();
|
||||||
|
|
||||||
let performanceData: PerformanceData;
|
let performanceStore = createPerformanceStore();
|
||||||
let viewerPerformance = createPerformanceStore();
|
|
||||||
|
|
||||||
let res: Int32Array;
|
|
||||||
let activeNode: Node | undefined;
|
let activeNode: Node | undefined;
|
||||||
|
|
||||||
let updateViewer: (arg: Int32Array) => void;
|
let graphResult: Int32Array;
|
||||||
|
|
||||||
let graph = localStorage.getItem("graph")
|
let graph = localStorage.getItem("graph")
|
||||||
? JSON.parse(localStorage.getItem("graph")!)
|
? JSON.parse(localStorage.getItem("graph")!)
|
||||||
@ -69,23 +66,21 @@
|
|||||||
try {
|
try {
|
||||||
let a = performance.now();
|
let a = performance.now();
|
||||||
// res = await remoteRuntime.execute(_graph, _settings);
|
// res = await remoteRuntime.execute(_graph, _settings);
|
||||||
let res = await workerRuntime.execute(_graph, _settings);
|
graphResult = await workerRuntime.execute(_graph, _settings);
|
||||||
let b = performance.now();
|
let b = performance.now();
|
||||||
updateViewer(res);
|
|
||||||
let perfData = await workerRuntime.getPerformanceData();
|
let perfData = await workerRuntime.getPerformanceData();
|
||||||
let lastRun = perfData.runs?.at(-1);
|
let lastRun = perfData.at(-1);
|
||||||
if (lastRun) {
|
if (lastRun) {
|
||||||
perfData.total["worker-transfer"] = b - a - lastRun.runtime[0];
|
|
||||||
lastRun["worker-transfer"] = [b - a - lastRun.runtime[0]];
|
lastRun["worker-transfer"] = [b - a - lastRun.runtime[0]];
|
||||||
|
performanceStore.mergeData(lastRun);
|
||||||
}
|
}
|
||||||
performanceData = perfData;
|
|
||||||
isWorking = false;
|
isWorking = false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("errors", error);
|
console.log("errors", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
viewerPerformance.stopRun();
|
performanceStore.stopRun();
|
||||||
viewerPerformance.startRun();
|
performanceStore.startRun();
|
||||||
|
|
||||||
if (unfinished) {
|
if (unfinished) {
|
||||||
let d = unfinished;
|
let d = unfinished;
|
||||||
@ -115,8 +110,8 @@
|
|||||||
<Grid.Row>
|
<Grid.Row>
|
||||||
<Grid.Cell>
|
<Grid.Cell>
|
||||||
<Viewer
|
<Viewer
|
||||||
bind:update={updateViewer}
|
result={graphResult}
|
||||||
perf={viewerPerformance}
|
perf={performanceStore}
|
||||||
centerCamera={$AppSettings.centerCamera}
|
centerCamera={$AppSettings.centerCamera}
|
||||||
/>
|
/>
|
||||||
</Grid.Cell>
|
</Grid.Cell>
|
||||||
@ -168,11 +163,8 @@
|
|||||||
hidden={!$AppSettings.showPerformancePanel}
|
hidden={!$AppSettings.showPerformancePanel}
|
||||||
icon="i-tabler-brand-speedtest"
|
icon="i-tabler-brand-speedtest"
|
||||||
>
|
>
|
||||||
{#if performanceData}
|
{#if $performanceStore}
|
||||||
<PerformanceViewer
|
<PerformanceViewer data={$performanceStore} />
|
||||||
data={performanceData}
|
|
||||||
viewer={$viewerPerformance}
|
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
</Panel>
|
</Panel>
|
||||||
<Panel
|
<Panel
|
||||||
|
@ -36,19 +36,26 @@ export interface RuntimeExecutor {
|
|||||||
execute: (graph: Graph, settings: Record<string, unknown>) => Promise<Int32Array>;
|
execute: (graph: Graph, settings: Record<string, unknown>) => Promise<Int32Array>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuntimeCache {
|
export interface RuntimeCache<T = unknown> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of items that can be stored in the cache
|
||||||
|
* @remarks When the cache size exceeds this value, the oldest items should be removed
|
||||||
|
*/
|
||||||
|
size: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the value for the given key
|
* Get the value for the given key
|
||||||
* @param key - The key to get the value for
|
* @param key - The key to get the value for
|
||||||
* @returns The value for the given key, or undefined if no such value exists
|
* @returns The value for the given key, or undefined if no such value exists
|
||||||
*/
|
*/
|
||||||
get: (key: string) => unknown | undefined;
|
get: (key: string) => T | undefined;
|
||||||
/**
|
/**
|
||||||
* Set the value for the given key
|
* Set the value for the given key
|
||||||
* @param key - The key to set the value for
|
* @param key - The key to set the value for
|
||||||
* @param value - The value to set
|
* @param value - The value to set
|
||||||
*/
|
*/
|
||||||
set: (key: string, value: unknown) => void;
|
set: (key: string, value: T) => void;
|
||||||
/**
|
/**
|
||||||
* Clear the cache
|
* Clear the cache
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user