feat: add benchmark settings panel
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m59s
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 1m59s
This commit is contained in:
48
app/src/lib/performance/BarSplit.svelte
Normal file
48
app/src/lib/performance/BarSplit.svelte
Normal file
@@ -0,0 +1,48 @@
|
||||
<script lang="ts">
|
||||
export let labels: string[] = [];
|
||||
export let values: number[] = [];
|
||||
|
||||
$: total = values.reduce((acc, v) => acc + v, 0);
|
||||
|
||||
let colors = ["red", "green", "blue"];
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="bars">
|
||||
{#each values as value, i}
|
||||
<div class="bar bg-{colors[i]}" style="width: {(value / total) * 100}%;">
|
||||
{Math.round(value)}ms
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="labels mt-2">
|
||||
{#each values as _label, i}
|
||||
<div class="text-{colors[i]}">{labels[i]}</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<span
|
||||
class="bg-red bg-green bg-yellow bg-blue text-red text-green text-yellow text-blue"
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
margin-block: 1em;
|
||||
}
|
||||
|
||||
.bars {
|
||||
height: 20px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bar {
|
||||
height: 100%;
|
||||
color: black;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0.8em;
|
||||
padding-left: 0.4em;
|
||||
}
|
||||
</style>
|
||||
@@ -4,6 +4,7 @@
|
||||
import { Checkbox } from "@nodes/ui";
|
||||
import localStore from "$lib/helpers/localStore";
|
||||
import { type PerformanceData } from "./store";
|
||||
import BarSplit from "./BarSplit.svelte";
|
||||
|
||||
export let data: PerformanceData;
|
||||
|
||||
@@ -42,7 +43,7 @@
|
||||
}
|
||||
|
||||
function getLast(key: string) {
|
||||
return data.at(-1)?.[key][0] || 0;
|
||||
return data.at(-1)?.[key]?.[0] || 0;
|
||||
}
|
||||
|
||||
function getLasts() {
|
||||
@@ -53,13 +54,13 @@
|
||||
if (onlyLast) {
|
||||
return (
|
||||
getLast("runtime") +
|
||||
getLast("create-geometries") +
|
||||
getLast("update-geometries") +
|
||||
getLast("worker-transfer")
|
||||
);
|
||||
}
|
||||
return (
|
||||
getAverage("runtime") +
|
||||
getAverage("create-geometries") +
|
||||
getAverage("update-geometries") +
|
||||
getAverage("worker-transfer")
|
||||
);
|
||||
}
|
||||
@@ -73,7 +74,7 @@
|
||||
const viewerKeys = [
|
||||
"total-vertices",
|
||||
"total-faces",
|
||||
"create-geometries",
|
||||
"update-geometries",
|
||||
"split-result",
|
||||
];
|
||||
|
||||
@@ -116,8 +117,8 @@
|
||||
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)
|
||||
run["update-geometries"].reduce((acc, v) => acc + v, 0) +
|
||||
(run["worker-transfer"]?.reduce((acc, v) => acc + v, 0) || 0)
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -125,8 +126,8 @@
|
||||
return data.map((run) => {
|
||||
return (
|
||||
run["runtime"][0] +
|
||||
run["create-geometries"][0] +
|
||||
run["worker-transfer"][0]
|
||||
run["update-geometries"][0] +
|
||||
(run["worker-transfer"]?.[0] || 0)
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -147,6 +148,22 @@
|
||||
});
|
||||
}
|
||||
|
||||
function getSplitValues(): number[] {
|
||||
if (showAverage) {
|
||||
return [
|
||||
getAverage("worker-transfer"),
|
||||
getAverage("runtime"),
|
||||
getAverage("update-geometries"),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
getLast("worker-transfer"),
|
||||
getLast("runtime"),
|
||||
getLast("update-geometries"),
|
||||
];
|
||||
}
|
||||
|
||||
function getTitle(t: string) {
|
||||
if (t.includes("/")) {
|
||||
return `Node ${t.split("/").slice(-1).join("/")}`;
|
||||
@@ -159,7 +176,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#key $activeType && data}
|
||||
{#key $activeType}
|
||||
{#if $activeType === "cache-hit"}
|
||||
<Monitor
|
||||
title="Cache Hits"
|
||||
@@ -174,117 +191,122 @@
|
||||
points={constructPoints($activeType)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div class="p-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox id="show-total" bind:value={showAverage} />
|
||||
<label for="show-total">Show Average</label>
|
||||
</div>
|
||||
{#if data.length !== 0}
|
||||
<h3>General</h3>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
{round(getTotalPerformance(!showAverage))}<span>ms</span>
|
||||
</td>
|
||||
<td
|
||||
class:active={$activeType === "total"}
|
||||
on:click={() => ($activeType = "total")}
|
||||
>
|
||||
total<span
|
||||
>({Math.floor(
|
||||
1000 / getTotalPerformance(showAverage),
|
||||
)}fps)</span
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
{#each getPerformanceData(!showAverage) as [key, value]}
|
||||
<tr>
|
||||
<td>
|
||||
{round(value)}<span>ms</span>
|
||||
</td>
|
||||
<td
|
||||
class:active={$activeType === key}
|
||||
on:click={() => ($activeType = key)}
|
||||
>
|
||||
{key}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
|
||||
<tr>
|
||||
<td>{data.length}</td>
|
||||
<td>Samples</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<h3>Nodes</h3>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<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>
|
||||
<td>
|
||||
{round(value)}<span>ms</span>
|
||||
</td>
|
||||
|
||||
<td
|
||||
class:active={$activeType === key}
|
||||
on:click={() => ($activeType = key)}
|
||||
>
|
||||
{key.split("/").slice(-1).join("/")}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<h3>Viewer</h3>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<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>
|
||||
<td>
|
||||
{round(value)}<span>ms</span>
|
||||
</td>
|
||||
<td
|
||||
class:active={$activeType === key}
|
||||
on:click={() => ($activeType = key)}
|
||||
>
|
||||
{key.split("/").slice(-1).join("/")}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
{:else}
|
||||
<p>No runs available</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/key}
|
||||
|
||||
<div class="p-4 performance-tabler">
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox id="show-total" bind:value={showAverage} />
|
||||
<label for="show-total">Show Average</label>
|
||||
</div>
|
||||
|
||||
{#if data.length !== 0}
|
||||
<BarSplit
|
||||
labels={["worker-transfer", "runtime", "update-geometries"]}
|
||||
values={getSplitValues()}
|
||||
/>
|
||||
|
||||
<h3>General</h3>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
{round(getTotalPerformance(!showAverage))}<span>ms</span>
|
||||
</td>
|
||||
<td
|
||||
class:active={$activeType === "total"}
|
||||
on:click={() => ($activeType = "total")}
|
||||
>
|
||||
total<span
|
||||
>({Math.floor(1000 / getTotalPerformance(showAverage))}fps)</span
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
{#each getPerformanceData(!showAverage) as [key, value]}
|
||||
<tr>
|
||||
<td>
|
||||
{round(value)}<span>ms</span>
|
||||
</td>
|
||||
<td
|
||||
class:active={$activeType === key}
|
||||
on:click={() => ($activeType = key)}
|
||||
>
|
||||
{key}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
|
||||
<tr>
|
||||
<td>{data.length}</td>
|
||||
<td>Samples</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<h3>Nodes</h3>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<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>
|
||||
<td>
|
||||
{round(value)}<span>ms</span>
|
||||
</td>
|
||||
|
||||
<td
|
||||
class:active={$activeType === key}
|
||||
on:click={() => ($activeType = key)}
|
||||
>
|
||||
{key.split("/").slice(-1).join("/")}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<h3>Viewer</h3>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<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>
|
||||
<td>
|
||||
{round(value)}<span>ms</span>
|
||||
</td>
|
||||
<td
|
||||
class:active={$activeType === key}
|
||||
on:click={() => ($activeType = key)}
|
||||
>
|
||||
{key.split("/").slice(-1).join("/")}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
{:else}
|
||||
<p>No runs available</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
h3 {
|
||||
margin: 0;
|
||||
@@ -296,6 +318,9 @@
|
||||
opacity: 0.3;
|
||||
margin-left: 4px;
|
||||
}
|
||||
table {
|
||||
margin-bottom: 70px;
|
||||
}
|
||||
td {
|
||||
padding-right: 10px;
|
||||
padding-block: 5px;
|
||||
|
||||
@@ -6,15 +6,18 @@ export interface PerformanceStore extends Readable<PerformanceData> {
|
||||
startRun(): void;
|
||||
stopRun(): void;
|
||||
addPoint(name: string, value?: number): void;
|
||||
endPoint(name?: string): void;
|
||||
mergeData(data: PerformanceData[number]): void;
|
||||
get: () => PerformanceData;
|
||||
}
|
||||
|
||||
export function createPerformanceStore(): PerformanceStore {
|
||||
export function createPerformanceStore(id?: string): PerformanceStore {
|
||||
|
||||
let data: PerformanceData = [];
|
||||
|
||||
let currentRun: Record<string, number[]> | undefined;
|
||||
let temp: Record<string, number> | undefined;
|
||||
let lastPoint: string | undefined;
|
||||
|
||||
let set: (v: PerformanceData) => void;
|
||||
|
||||
@@ -23,22 +26,36 @@ export function createPerformanceStore(): PerformanceStore {
|
||||
});
|
||||
|
||||
function startRun() {
|
||||
if (currentRun) return;
|
||||
currentRun = {};
|
||||
lastPoint = undefined;
|
||||
temp = {
|
||||
start: performance.now()
|
||||
}
|
||||
}
|
||||
|
||||
function stopRun() {
|
||||
if (currentRun) {
|
||||
if (currentRun && temp) {
|
||||
currentRun["total"] = [performance.now() - temp.start];
|
||||
data.push(currentRun);
|
||||
data = data.slice(-100);
|
||||
currentRun = undefined;
|
||||
temp = undefined;
|
||||
if (set) set(data);
|
||||
}
|
||||
}
|
||||
|
||||
function addPoint(name: string, value: number) {
|
||||
function addPoint(name: string, value?: number) {
|
||||
if (!currentRun) return;
|
||||
currentRun[name] = currentRun[name] || [];
|
||||
currentRun[name].push(value);
|
||||
if (value === undefined) {
|
||||
if (temp) {
|
||||
lastPoint = name;
|
||||
temp[name] = performance.now();
|
||||
}
|
||||
} else {
|
||||
currentRun[name] = currentRun[name] || [];
|
||||
currentRun[name].push(value);
|
||||
}
|
||||
}
|
||||
|
||||
function get() {
|
||||
@@ -59,11 +76,21 @@ export function createPerformanceStore(): PerformanceStore {
|
||||
});
|
||||
}
|
||||
|
||||
function endPoint(name = lastPoint) {
|
||||
if (name === lastPoint) lastPoint = undefined;
|
||||
if (name && currentRun && temp && name in temp) {
|
||||
currentRun[name] = currentRun[name] || [];
|
||||
currentRun[name].push(performance.now() - temp[name]);
|
||||
delete temp[name];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
startRun,
|
||||
stopRun,
|
||||
addPoint,
|
||||
endPoint,
|
||||
mergeData,
|
||||
get
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user