diff --git a/app/src/lib/graph-interface/node/Node.svelte b/app/src/lib/graph-interface/node/Node.svelte
index 2fba499..f9b52b1 100644
--- a/app/src/lib/graph-interface/node/Node.svelte
+++ b/app/src/lib/graph-interface/node/Node.svelte
@@ -16,7 +16,7 @@
inView: boolean;
z: number;
};
- let { node, inView, z }: Props = $props();
+ let { node = $bindable(), inView, z }: Props = $props();
const isActive = $derived(graphState.activeNodeId === node.id);
const isSelected = $derived(graphState.selectedNodes.has(node.id));
@@ -67,4 +67,4 @@
/>
-
+
diff --git a/app/src/lib/graph-interface/node/NodeHTML.svelte b/app/src/lib/graph-interface/node/NodeHTML.svelte
index e0261b6..cd02ed2 100644
--- a/app/src/lib/graph-interface/node/NodeHTML.svelte
+++ b/app/src/lib/graph-interface/node/NodeHTML.svelte
@@ -1,5 +1,5 @@
@@ -27,12 +25,12 @@
($open.runtime = !$open.runtime)}
+ onclick={() => (open.value.runtime = !open.value.runtime)}
>
- | {$open.runtime ? "-" : "+"} runtime |
+ {open.value.runtime ? "-" : "+"} runtime |
{humanizeDuration(runtime || 1000)} |
- {#if $open.runtime}
+ {#if open.value.runtime}
|
@@ -40,13 +38,16 @@
|
{/if}
- ($open.fps = !$open.fps)}>
- | {$open.fps ? "-" : "+"} fps |
+
(open.value.fps = !open.value.fps)}
+ >
+ | {open.value.fps ? "-" : "+"} fps |
{Math.floor(fps[fps.length - 1])}fps
|
- {#if $open.fps}
+ {#if open.value.fps}
|
diff --git a/app/src/lib/result-viewer/geometryPool.ts b/app/src/lib/result-viewer/geometryPool.ts
index 1ecf794..960dc99 100644
--- a/app/src/lib/result-viewer/geometryPool.ts
+++ b/app/src/lib/result-viewer/geometryPool.ts
@@ -11,7 +11,7 @@ import {
} from "three";
function fastArrayHash(arr: Int32Array) {
- const sampleDistance = Math.max(Math.floor(arr.length / 100), 1);
+ const sampleDistance = Math.max(Math.floor(arr.length / 1000), 1);
const sampleCount = Math.floor(arr.length / sampleDistance);
let hash = new Int32Array(sampleCount);
@@ -40,6 +40,9 @@ export function createGeometryPool(parentScene: Group, material: Material) {
let hash = fastArrayHash(data);
let geometry = existingMesh ? existingMesh.geometry : new BufferGeometry();
+ if (existingMesh) {
+ existingMesh.visible = true;
+ }
// Extract data from the encoded array
// const geometryType = encodedData[index++];
@@ -121,7 +124,6 @@ export function createGeometryPool(parentScene: Group, material: Material) {
updateSingleGeometry(input, existingMesh || null);
} else if (existingMesh) {
existingMesh.visible = false;
- scene.remove(existingMesh);
}
}
return { totalVertices, totalFaces };
@@ -258,7 +260,6 @@ export function createInstancedGeometryPool(
updateSingleInstance(input, existingMesh || null);
} else if (existingMesh) {
existingMesh.visible = false;
- scene.remove(existingMesh);
}
}
return { totalVertices, totalFaces };
diff --git a/app/src/lib/runtime/runtime-executor-cache.ts b/app/src/lib/runtime/runtime-executor-cache.ts
index 59514b6..a77f223 100644
--- a/app/src/lib/runtime/runtime-executor-cache.ts
+++ b/app/src/lib/runtime/runtime-executor-cache.ts
@@ -1,19 +1,33 @@
import { type SyncCache } from "@nodarium/types";
export class MemoryRuntimeCache implements SyncCache {
+ private map = new Map();
+ size: number;
- private cache: [string, unknown][] = [];
- size = 50;
+ constructor(size = 50) {
+ this.size = size;
+ }
get(key: string): T | undefined {
- return this.cache.find(([k]) => k === key)?.[1] as T;
- }
- set(key: string, value: T): void {
- this.cache.push([key, value]);
- this.cache = this.cache.slice(-this.size);
- }
- clear(): void {
- this.cache = [];
+ if (!this.map.has(key)) return undefined;
+ const value = this.map.get(key) as T;
+ this.map.delete(key);
+ this.map.set(key, value);
+ return value;
}
+ set(key: string, value: T): void {
+ if (this.map.has(key)) {
+ this.map.delete(key);
+ }
+ this.map.set(key, value);
+ while (this.map.size > this.size) {
+ const oldestKey = this.map.keys().next().value as string;
+ this.map.delete(oldestKey);
+ }
+ }
+
+ clear(): void {
+ this.map.clear();
+ }
}
diff --git a/app/src/lib/runtime/worker-runtime-executor-backend.ts b/app/src/lib/runtime/worker-runtime-executor-backend.ts
index 97478b4..b26cc07 100644
--- a/app/src/lib/runtime/worker-runtime-executor-backend.ts
+++ b/app/src/lib/runtime/worker-runtime-executor-backend.ts
@@ -2,11 +2,13 @@ import { MemoryRuntimeExecutor } from "./runtime-executor";
import { RemoteNodeRegistry, IndexDBCache } from "@nodarium/registry";
import type { Graph } from "@nodarium/types";
import { createPerformanceStore } from "@nodarium/utils";
+import { MemoryRuntimeCache } from "./runtime-executor-cache";
const indexDbCache = new IndexDBCache("node-registry");
const nodeRegistry = new RemoteNodeRegistry("", indexDbCache);
-const executor = new MemoryRuntimeExecutor(nodeRegistry);
+const cache = new MemoryRuntimeCache()
+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 d992265..60d619f 100644
--- a/app/src/routes/+page.svelte
+++ b/app/src/routes/+page.svelte
@@ -88,13 +88,10 @@
randomSeed: { type: "boolean", value: false },
});
- let runIndex = 0;
-
async function update(
g: Graph,
s: Record = $state.snapshot(graphSettings),
) {
- runIndex++;
performanceStore.startRun();
try {
let a = performance.now();
diff --git a/packages/utils/src/fastHash.test.ts b/packages/utils/src/fastHash.test.ts
index ae8d729..291960f 100644
--- a/packages/utils/src/fastHash.test.ts
+++ b/packages/utils/src/fastHash.test.ts
@@ -1,5 +1,5 @@
import { test, expect } from 'vitest';
-import { fastHashArray, fastHashString } from './fastHash';
+import { fastHashArrayBuffer, fastHashString } from './fastHash';
test('fastHashString doesnt produce clashes', () => {
const hashA = fastHashString('abcdef');
@@ -14,10 +14,10 @@ test("fastHashArray doesnt product collisions", () => {
const a = new Int32Array(1000);
- const hash_a = fastHashArray(a.buffer);
+ const hash_a = fastHashArrayBuffer(a);
a[0] = 1;
- const hash_b = fastHashArray(a.buffer);
+ const hash_b = fastHashArrayBuffer(a);
expect(hash_a).not.toEqual(hash_b);
@@ -28,13 +28,13 @@ test('fastHashArray is fast(ish) < 20ms', () => {
const a = new Int32Array(10_000);
const t0 = performance.now();
- fastHashArray(a.buffer);
+ fastHashArrayBuffer(a);
const t1 = performance.now();
a[0] = 1;
- fastHashArray(a.buffer);
+ fastHashArrayBuffer(a);
const t2 = performance.now();
@@ -48,7 +48,7 @@ test('fastHashArray is deterministic', () => {
a[42] = 69;
const b = new Int32Array(1000);
b[42] = 69;
- const hashA = fastHashArray(a.buffer);
- const hashB = fastHashArray(b.buffer);
+ const hashA = fastHashArrayBuffer(a);
+ const hashB = fastHashArrayBuffer(b);
expect(hashA).toEqual(hashB);
});
diff --git a/packages/utils/src/fastHash.ts b/packages/utils/src/fastHash.ts
index 8feba27..8259bcd 100644
--- a/packages/utils/src/fastHash.ts
+++ b/packages/utils/src/fastHash.ts
@@ -1,156 +1,45 @@
-// https://github.com/6502/sha256/blob/main/sha256.js
-function sha256(data?: string | Int32Array) {
- let h0 = 0x6a09e667,
- h1 = 0xbb67ae85,
- h2 = 0x3c6ef372,
- h3 = 0xa54ff53a,
- h4 = 0x510e527f,
- h5 = 0x9b05688c,
- h6 = 0x1f83d9ab,
- h7 = 0x5be0cd19,
- tsz = 0,
- bp = 0;
- const k = [
- 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
- 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
- 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
- 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
- 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
- 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
- 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
- 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
- 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
- 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
- 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
- ],
- rrot = (x: number, n: number) => (x >>> n) | (x << (32 - n)),
- w = new Uint32Array(64),
- buf = new Uint8Array(64),
- process = () => {
- for (let j = 0, r = 0; j < 16; j++, r += 4) {
- w[j] =
- (buf[r] << 24) | (buf[r + 1] << 16) | (buf[r + 2] << 8) | buf[r + 3];
- }
- for (let j = 16; j < 64; j++) {
- let s0 = rrot(w[j - 15], 7) ^ rrot(w[j - 15], 18) ^ (w[j - 15] >>> 3);
- let s1 = rrot(w[j - 2], 17) ^ rrot(w[j - 2], 19) ^ (w[j - 2] >>> 10);
- w[j] = (w[j - 16] + s0 + w[j - 7] + s1) | 0;
- }
- let a = h0,
- b = h1,
- c = h2,
- d = h3,
- e = h4,
- f = h5,
- g = h6,
- h = h7;
- for (let j = 0; j < 64; j++) {
- let S1 = rrot(e, 6) ^ rrot(e, 11) ^ rrot(e, 25),
- ch = (e & f) ^ (~e & g),
- t1 = (h + S1 + ch + k[j] + w[j]) | 0,
- S0 = rrot(a, 2) ^ rrot(a, 13) ^ rrot(a, 22),
- maj = (a & b) ^ (a & c) ^ (b & c),
- t2 = (S0 + maj) | 0;
- h = g;
- g = f;
- f = e;
- e = (d + t1) | 0;
- d = c;
- c = b;
- b = a;
- a = (t1 + t2) | 0;
- }
- h0 = (h0 + a) | 0;
- h1 = (h1 + b) | 0;
- h2 = (h2 + c) | 0;
- h3 = (h3 + d) | 0;
- h4 = (h4 + e) | 0;
- h5 = (h5 + f) | 0;
- h6 = (h6 + g) | 0;
- h7 = (h7 + h) | 0;
- bp = 0;
- },
- add = (input: string | Int32Array) => {
- const data =
- typeof input === "string"
- ? typeof TextEncoder === "undefined"
- ? //@ts-ignore
- Buffer.from(input)
- : new TextEncoder().encode(input)
- : input;
+export function fastHashArrayBuffer(input: string | Int32Array): string {
+ const mask = (1n << 64n) - 1n
- for (let i = 0; i < data.length; i++) {
- buf[bp++] = data[i];
- if (bp === 64) process();
- }
- tsz += data.length;
- },
- digest = () => {
- buf[bp++] = 0x80;
- if (bp == 64) process();
- if (bp + 8 > 64) {
- while (bp < 64) buf[bp++] = 0x00;
- process();
- }
- while (bp < 58) buf[bp++] = 0x00;
- // Max number of bytes is 35,184,372,088,831
- let L = tsz * 8;
- buf[bp++] = (L / 1099511627776) & 255;
- buf[bp++] = (L / 4294967296) & 255;
- buf[bp++] = L >>> 24;
- buf[bp++] = (L >>> 16) & 255;
- buf[bp++] = (L >>> 8) & 255;
- buf[bp++] = L & 255;
- process();
- let reply = new Uint8Array(32);
- reply[0] = h0 >>> 24;
- reply[1] = (h0 >>> 16) & 255;
- reply[2] = (h0 >>> 8) & 255;
- reply[3] = h0 & 255;
- reply[4] = h1 >>> 24;
- reply[5] = (h1 >>> 16) & 255;
- reply[6] = (h1 >>> 8) & 255;
- reply[7] = h1 & 255;
- reply[8] = h2 >>> 24;
- reply[9] = (h2 >>> 16) & 255;
- reply[10] = (h2 >>> 8) & 255;
- reply[11] = h2 & 255;
- reply[12] = h3 >>> 24;
- reply[13] = (h3 >>> 16) & 255;
- reply[14] = (h3 >>> 8) & 255;
- reply[15] = h3 & 255;
- reply[16] = h4 >>> 24;
- reply[17] = (h4 >>> 16) & 255;
- reply[18] = (h4 >>> 8) & 255;
- reply[19] = h4 & 255;
- reply[20] = h5 >>> 24;
- reply[21] = (h5 >>> 16) & 255;
- reply[22] = (h5 >>> 8) & 255;
- reply[23] = h5 & 255;
- reply[24] = h6 >>> 24;
- reply[25] = (h6 >>> 16) & 255;
- reply[26] = (h6 >>> 8) & 255;
- reply[27] = h6 & 255;
- reply[28] = h7 >>> 24;
- reply[29] = (h7 >>> 16) & 255;
- reply[30] = (h7 >>> 8) & 255;
- reply[31] = h7 & 255;
- let res = "";
- reply.forEach((x) => (res += ("0" + x.toString(16)).slice(-2)));
- return res;
- };
+ // FNV-1a 64-bit constants
+ let h = 0xcbf29ce484222325n // offset basis
+ const FNV_PRIME = 0x100000001b3n
- if (data) add(data);
+ // get bytes for string or Int32Array
+ let bytes: Uint8Array
+ if (typeof input === "string") {
+ // utf-8 encoding
+ bytes = new TextEncoder().encode(input)
+ } else {
+ // Int32Array -> bytes (little-endian)
+ bytes = new Uint8Array(input.length * 4)
+ for (let i = 0; i < input.length; i++) {
+ const v = input[i] >>> 0 // ensure unsigned 32-bit
+ const base = i * 4
+ bytes[base] = v & 0xff
+ bytes[base + 1] = (v >>> 8) & 0xff
+ bytes[base + 2] = (v >>> 16) & 0xff
+ bytes[base + 3] = (v >>> 24) & 0xff
+ }
+ }
- return { add, digest };
+ // FNV-1a byte-wise
+ for (let i = 0; i < bytes.length; i++) {
+ h = (h ^ BigInt(bytes[i])) & mask
+ h = (h * FNV_PRIME) & mask
+ }
+
+ // MurmurHash3's fmix64 finalizer (good avalanche)
+ h ^= h >> 33n
+ h = (h * 0xff51afd7ed558ccdn) & mask
+ h ^= h >> 33n
+ h = (h * 0xc4ceb9fe1a85ec53n) & mask
+ h ^= h >> 33n
+
+ // to 16-char hex
+ return h.toString(16).padStart(16, "0").slice(-16)
}
-export function fastHashArrayBuffer(buffer: string | Int32Array): string {
- return sha256(buffer).digest();
-}
-
-// Shamelessly copied from
-// https://stackoverflow.com/a/8831937
export function fastHashString(input: string) {
if (input.length === 0) return 0;
@@ -162,20 +51,3 @@ export function fastHashString(input: string) {
return hash;
}
-
-export function fastHash(input: (string | Int32Array | number)[]) {
- const s = sha256();
-
- for (let i = 0; i < input.length; i++) {
- const v = input[i];
- if (typeof v === "string") {
- s.add(v);
- } else if (v instanceof Int32Array) {
- s.add(v);
- } else {
- s.add(v.toString());
- }
- }
-
- return s.digest();
-}
|