feat: implement performance view
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m10s
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m10s
This commit is contained in:
68
app/src/lib/performance/Monitor.svelte
Normal file
68
app/src/lib/performance/Monitor.svelte
Normal file
@@ -0,0 +1,68 @@
|
||||
<script lang="ts">
|
||||
export let points: number[];
|
||||
|
||||
$: max = Math.max(...points);
|
||||
$: min = Math.min(...points);
|
||||
function constructPath() {
|
||||
return points
|
||||
.map((point, i) => {
|
||||
const x = (i / (points.length - 1)) * 100;
|
||||
const y = 100 - ((point - min) / (max - min)) * 100;
|
||||
return `${x},${y}`;
|
||||
})
|
||||
.join(" ");
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<p>Runtime Execution</p>
|
||||
<span class="min">{min}ms</span>
|
||||
<span class="max">{max}ms</span>
|
||||
<svg preserveAspectRatio="none" viewBox="0 0 100 100">
|
||||
<polyline vector-effect="non-scaling-stroke" points={constructPath()} />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
span {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
font-size: 0.8em;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.max {
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.min {
|
||||
bottom: 5px;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
position: relative;
|
||||
border-bottom: solid thin var(--outline);
|
||||
display: flex;
|
||||
}
|
||||
p {
|
||||
margin: 0px;
|
||||
top: 3px;
|
||||
left: 5px;
|
||||
font-size: 0.9em;
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
}
|
||||
svg {
|
||||
height: 124px;
|
||||
margin: 24px 0px;
|
||||
border-top: solid thin var(--outline);
|
||||
border-bottom: solid thin var(--outline);
|
||||
width: 100%;
|
||||
}
|
||||
polyline {
|
||||
fill: none;
|
||||
stroke: var(--layer-3);
|
||||
opacity: 0.5;
|
||||
stroke-width: 1;
|
||||
}
|
||||
</style>
|
||||
@@ -1,17 +1,122 @@
|
||||
<script lang="ts">
|
||||
import type { PerformanceData } from ".";
|
||||
import { browser } from "$app/environment";
|
||||
import Monitor from "./Monitor.svelte";
|
||||
import { humanizeNumber } from "$lib/helpers";
|
||||
|
||||
type PerformanceData = {
|
||||
total: Record<string, number>;
|
||||
runs: Record<string, number[]>[];
|
||||
};
|
||||
|
||||
export let data: PerformanceData;
|
||||
export let viewer: PerformanceData;
|
||||
|
||||
function getPerformanceData() {
|
||||
return Object.entries(data.total).sort((a, b) => b[1] - a[1]);
|
||||
return Object.entries(data.total)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.filter(([key]) => !key.startsWith("node/"));
|
||||
}
|
||||
|
||||
function getNodePerformanceData() {
|
||||
return Object.entries(data.total)
|
||||
.filter(([key]) => key.startsWith("node/"))
|
||||
.sort((a, b) => b[1] - a[1]);
|
||||
}
|
||||
|
||||
function getViewerPerformanceData() {
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if data.runs.length !== 0}
|
||||
{#each getPerformanceData() as [key, value]}
|
||||
<p>{key}: {Math.floor(value * 100) / 100}ms</p>
|
||||
{/each}
|
||||
{:else}
|
||||
<p>No runs available</p>
|
||||
{/if}
|
||||
{#key data}
|
||||
{#if browser}
|
||||
<Monitor points={constructPoints("total")} />
|
||||
{/if}
|
||||
|
||||
<div class="px-4">
|
||||
{#if data.runs.length !== 0}
|
||||
<h3>General</h3>
|
||||
<table>
|
||||
{#each getPerformanceData() as [key, value]}
|
||||
<tr>
|
||||
<td>
|
||||
{Math.floor(value * 100) / 100}<span>ms</span>
|
||||
</td>
|
||||
<td>
|
||||
{key}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
|
||||
<h3>Nodes</h3>
|
||||
{#each getNodePerformanceData() as [key, value]}
|
||||
<tr>
|
||||
<td>
|
||||
{Math.floor(value * 100) / 100}<span>ms</span>
|
||||
</td>
|
||||
<td>
|
||||
{key.split("/").slice(-1).join("/")}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
|
||||
{#if viewer.runs.length}
|
||||
<h3>Viewer</h3>
|
||||
<tr>
|
||||
<td>{humanizeNumber(viewer.runs.at(-1)?.["total-vertices"])}</td>
|
||||
<td>Vertices</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{humanizeNumber(viewer.runs.at(-1)?.["total-faces"])}</td>
|
||||
<td>Faces</td>
|
||||
</tr>
|
||||
{#each getViewerPerformanceData() 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>
|
||||
{:else}
|
||||
<p>No runs available</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/key}
|
||||
|
||||
<style>
|
||||
h3 {
|
||||
margin: 0;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0.2em;
|
||||
margin-left: 3px;
|
||||
}
|
||||
span {
|
||||
opacity: 0.3;
|
||||
margin-left: 4px;
|
||||
}
|
||||
td {
|
||||
padding-right: 10px;
|
||||
padding-block: 5px;
|
||||
}
|
||||
tr > td:nth-child(1) {
|
||||
text-align: right;
|
||||
}
|
||||
tr > td:nth-child(2) {
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,68 +1,2 @@
|
||||
import { readable, type Readable } from "svelte/store";
|
||||
|
||||
export type PerformanceData = {
|
||||
total: Record<string, number>;
|
||||
runs: Record<string, number[]>[];
|
||||
}
|
||||
export interface PerformanceStore extends Readable<PerformanceData> {
|
||||
startRun(): void;
|
||||
stopRun(): void;
|
||||
addPoint(name: string, value?: number): void;
|
||||
get: () => PerformanceData;
|
||||
}
|
||||
|
||||
export function createPerformanceStore(): PerformanceStore {
|
||||
|
||||
let data: PerformanceData = { total: {}, runs: [] };
|
||||
|
||||
let currentRun: Record<string, number[]> | undefined;
|
||||
|
||||
let set: (v: PerformanceData) => void;
|
||||
|
||||
const { subscribe } = readable<PerformanceData>({ total: {}, runs: [] }, (_set) => {
|
||||
set = _set;
|
||||
});
|
||||
|
||||
function startRun() {
|
||||
currentRun = {};
|
||||
}
|
||||
|
||||
function stopRun() {
|
||||
if (currentRun) {
|
||||
// Calculate total
|
||||
Object.keys(currentRun).forEach((name) => {
|
||||
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);
|
||||
currentRun = undefined;
|
||||
if (set) set(data);
|
||||
}
|
||||
}
|
||||
|
||||
function addPoint(name: string, value: number) {
|
||||
if (!currentRun) return;
|
||||
currentRun[name] = currentRun[name] || [];
|
||||
currentRun[name].push(value);
|
||||
}
|
||||
|
||||
function get() {
|
||||
return data;
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
startRun,
|
||||
stopRun,
|
||||
addPoint,
|
||||
get
|
||||
}
|
||||
}
|
||||
|
||||
export * from "./store";
|
||||
export { default as PerformanceViewer } from "./PerformanceViewer.svelte";
|
||||
|
||||
67
app/src/lib/performance/store.ts
Normal file
67
app/src/lib/performance/store.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { readable, type Readable } from "svelte/store";
|
||||
|
||||
export type PerformanceData = {
|
||||
total: Record<string, number>;
|
||||
runs: Record<string, number[]>[];
|
||||
}
|
||||
|
||||
export interface PerformanceStore extends Readable<PerformanceData> {
|
||||
startRun(): void;
|
||||
stopRun(): void;
|
||||
addPoint(name: string, value?: number): void;
|
||||
get: () => PerformanceData;
|
||||
}
|
||||
|
||||
export function createPerformanceStore(): PerformanceStore {
|
||||
|
||||
let data: PerformanceData = { total: {}, runs: [] };
|
||||
|
||||
let currentRun: Record<string, number[]> | undefined;
|
||||
|
||||
let set: (v: PerformanceData) => void;
|
||||
|
||||
const { subscribe } = readable<PerformanceData>({ total: {}, runs: [] }, (_set) => {
|
||||
set = _set;
|
||||
});
|
||||
|
||||
function startRun() {
|
||||
currentRun = {};
|
||||
}
|
||||
|
||||
function stopRun() {
|
||||
if (currentRun) {
|
||||
// Calculate total
|
||||
Object.keys(currentRun).forEach((name) => {
|
||||
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);
|
||||
currentRun = undefined;
|
||||
if (set) set(data);
|
||||
}
|
||||
}
|
||||
|
||||
function addPoint(name: string, value: number) {
|
||||
if (!currentRun) return;
|
||||
currentRun[name] = currentRun[name] || [];
|
||||
currentRun[name].push(value);
|
||||
}
|
||||
|
||||
function get() {
|
||||
return data;
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
startRun,
|
||||
stopRun,
|
||||
addPoint,
|
||||
get
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user