From d3c1fc31035eba74aff516723389febecf8f870a Mon Sep 17 00:00:00 2001
From: KernelDeimos <7225168+KernelDeimos@users.noreply.github.com>
Date: Fri, 19 Dec 2025 01:13:02 -0500
Subject: [PATCH] perf: more benchmarks
---
.../services/file-cache/FileTracker.bench.js | 182 ++++++++++++++++++
src/backend/src/util/datautil.bench.js | 165 ++++++++++++++++
src/backend/src/util/opmath.bench.js | 122 ++++++++++++
3 files changed, 469 insertions(+)
create mode 100644 src/backend/src/services/file-cache/FileTracker.bench.js
create mode 100644 src/backend/src/util/datautil.bench.js
create mode 100644 src/backend/src/util/opmath.bench.js
diff --git a/src/backend/src/services/file-cache/FileTracker.bench.js b/src/backend/src/services/file-cache/FileTracker.bench.js
new file mode 100644
index 000000000..0e6a517a7
--- /dev/null
+++ b/src/backend/src/services/file-cache/FileTracker.bench.js
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2024-present Puter Technologies Inc.
+ *
+ * This file is part of Puter.
+ *
+ * Puter is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import { bench, describe } from 'vitest';
+const { FileTracker } = require('./FileTracker');
+
+// Helper to create a tracker with some access history
+const createTrackerWithHistory = (accessCount) => {
+ const tracker = new FileTracker({ key: 'test-key', size: 1024 });
+ for ( let i = 0; i < accessCount; i++ ) {
+ tracker.touch();
+ }
+ return tracker;
+};
+
+describe('FileTracker - Construction', () => {
+ bench('create new FileTracker', () => {
+ new FileTracker({ key: `test-key-${ Math.random()}`, size: 1024 });
+ });
+
+ bench('create multiple FileTrackers', () => {
+ for ( let i = 0; i < 100; i++ ) {
+ new FileTracker({ key: `key-${i}`, size: i * 100 });
+ }
+ });
+});
+
+describe('FileTracker - touch() operation', () => {
+ bench('touch() on new tracker', () => {
+ const tracker = new FileTracker({ key: 'test', size: 1024 });
+ for ( let i = 0; i < 1000; i++ ) {
+ tracker.touch();
+ }
+ });
+
+ bench('touch() with EWMA calculation', () => {
+ const tracker = new FileTracker({ key: 'test', size: 1024 });
+ // Pre-warm with some touches
+ for ( let i = 0; i < 10; i++ ) {
+ tracker.touch();
+ }
+ // Benchmark steady-state touches
+ for ( let i = 0; i < 1000; i++ ) {
+ tracker.touch();
+ }
+ });
+});
+
+describe('FileTracker - score calculation', () => {
+ bench('score on fresh tracker', () => {
+ const tracker = new FileTracker({ key: 'test', size: 1024 });
+ tracker.touch(); // Need at least one touch for meaningful score
+ for ( let i = 0; i < 1000; i++ ) {
+ void tracker.score;
+ }
+ });
+
+ bench('score on tracker with history (10 accesses)', () => {
+ const tracker = createTrackerWithHistory(10);
+ for ( let i = 0; i < 1000; i++ ) {
+ void tracker.score;
+ }
+ });
+
+ bench('score on tracker with history (100 accesses)', () => {
+ const tracker = createTrackerWithHistory(100);
+ for ( let i = 0; i < 1000; i++ ) {
+ void tracker.score;
+ }
+ });
+});
+
+describe('FileTracker - age calculation', () => {
+ bench('age getter', () => {
+ const tracker = new FileTracker({ key: 'test', size: 1024 });
+ for ( let i = 0; i < 10000; i++ ) {
+ void tracker.age;
+ }
+ });
+});
+
+describe('FileTracker - Cache eviction simulation', () => {
+ bench('compare scores of multiple trackers', () => {
+ // Simulate cache with 100 items
+ const trackers = [];
+ for ( let i = 0; i < 100; i++ ) {
+ const tracker = new FileTracker({ key: `file-${i}`, size: i * 100 });
+ // Simulate varying access patterns
+ const accessCount = Math.floor(Math.random() * 20);
+ for ( let j = 0; j < accessCount; j++ ) {
+ tracker.touch();
+ }
+ trackers.push(tracker);
+ }
+
+ // Find lowest score (eviction candidate)
+ for ( let i = 0; i < 100; i++ ) {
+ let minScore = Infinity;
+ let evictCandidate = null;
+ for ( const tracker of trackers ) {
+ const score = tracker.score;
+ if ( score < minScore ) {
+ minScore = score;
+ evictCandidate = tracker;
+ }
+ }
+ }
+ });
+
+ bench('sort trackers by score (eviction ordering)', () => {
+ const trackers = [];
+ for ( let i = 0; i < 50; i++ ) {
+ const tracker = new FileTracker({ key: `file-${i}`, size: i * 100 });
+ for ( let j = 0; j < i % 10; j++ ) {
+ tracker.touch();
+ }
+ trackers.push(tracker);
+ }
+
+ // Sort by score
+ for ( let i = 0; i < 10; i++ ) {
+ [...trackers].sort((a, b) => a.score - b.score);
+ }
+ });
+});
+
+describe('FileTracker - Real-world access patterns', () => {
+ bench('hot file pattern (frequent access)', () => {
+ const tracker = new FileTracker({ key: 'hot-file', size: 1024 });
+ for ( let i = 0; i < 1000; i++ ) {
+ tracker.touch();
+ if ( i % 10 === 0 ) {
+ void tracker.score;
+ }
+ }
+ });
+
+ bench('cold file pattern (rare access)', () => {
+ const tracker = new FileTracker({ key: 'cold-file', size: 1024 });
+ tracker.touch();
+ for ( let i = 0; i < 1000; i++ ) {
+ void tracker.score;
+ void tracker.age;
+ }
+ });
+
+ bench('mixed access with score checks', () => {
+ const trackers = [];
+ for ( let i = 0; i < 20; i++ ) {
+ trackers.push(new FileTracker({ key: `file-${i}`, size: 1024 }));
+ }
+
+ for ( let i = 0; i < 500; i++ ) {
+ // Random access
+ const idx = Math.floor(Math.random() * trackers.length);
+ trackers[idx].touch();
+
+ // Periodic eviction check
+ if ( i % 50 === 0 ) {
+ for ( const t of trackers ) {
+ void t.score;
+ }
+ }
+ }
+ });
+});
diff --git a/src/backend/src/util/datautil.bench.js b/src/backend/src/util/datautil.bench.js
new file mode 100644
index 000000000..f3f21366c
--- /dev/null
+++ b/src/backend/src/util/datautil.bench.js
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2024-present Puter Technologies Inc.
+ *
+ * This file is part of Puter.
+ *
+ * Puter is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import { bench, describe } from 'vitest';
+const { stringify_serializable_object, hash_serializable_object } = require('./datautil');
+
+// Test data generators
+const createFlatObject = (size) => {
+ const obj = {};
+ for ( let i = 0; i < size; i++ ) {
+ obj[`key${i}`] = `value${i}`;
+ }
+ return obj;
+};
+
+const createNestedObject = (depth, breadth) => {
+ if ( depth === 0 ) {
+ return { leaf: 'value' };
+ }
+ const obj = {};
+ for ( let i = 0; i < breadth; i++ ) {
+ obj[`level${depth}_child${i}`] = createNestedObject(depth - 1, breadth);
+ }
+ return obj;
+};
+
+const createMixedObject = () => ({
+ string: 'hello world',
+ number: 42,
+ boolean: true,
+ null: null,
+ array: [1, 2, 3, { nested: 'array' }],
+ nested: {
+ deep: {
+ value: 'found',
+ numbers: [1, 2, 3],
+ },
+ },
+});
+
+// Objects with different key orderings (should produce same hash)
+const objA = { z: 1, a: 2, m: 3 };
+const objB = { a: 2, m: 3, z: 1 };
+const objC = { m: 3, z: 1, a: 2 };
+
+describe('stringify_serializable_object - Flat objects', () => {
+ const small = createFlatObject(5);
+ const medium = createFlatObject(20);
+ const large = createFlatObject(100);
+
+ bench('small flat object (5 keys)', () => {
+ stringify_serializable_object(small);
+ });
+
+ bench('medium flat object (20 keys)', () => {
+ stringify_serializable_object(medium);
+ });
+
+ bench('large flat object (100 keys)', () => {
+ stringify_serializable_object(large);
+ });
+});
+
+describe('stringify_serializable_object - Nested objects', () => {
+ const shallow = createNestedObject(2, 3); // depth 2, 3 children each
+ const medium = createNestedObject(3, 3); // depth 3, 3 children each
+ const deep = createNestedObject(4, 2); // depth 4, 2 children each
+
+ bench('shallow nested (depth=2, breadth=3)', () => {
+ stringify_serializable_object(shallow);
+ });
+
+ bench('medium nested (depth=3, breadth=3)', () => {
+ stringify_serializable_object(medium);
+ });
+
+ bench('deep nested (depth=4, breadth=2)', () => {
+ stringify_serializable_object(deep);
+ });
+});
+
+describe('stringify_serializable_object - Mixed types', () => {
+ const mixed = createMixedObject();
+
+ bench('mixed type object', () => {
+ stringify_serializable_object(mixed);
+ });
+
+ bench('primitives', () => {
+ stringify_serializable_object('string');
+ stringify_serializable_object(42);
+ stringify_serializable_object(true);
+ stringify_serializable_object(null);
+ stringify_serializable_object(undefined);
+ });
+});
+
+describe('stringify_serializable_object - Key ordering normalization', () => {
+ bench('objects with different key orderings', () => {
+ // All should produce the same output
+ stringify_serializable_object(objA);
+ stringify_serializable_object(objB);
+ stringify_serializable_object(objC);
+ });
+});
+
+describe('stringify_serializable_object vs JSON.stringify', () => {
+ const obj = createFlatObject(20);
+
+ bench('stringify_serializable_object', () => {
+ stringify_serializable_object(obj);
+ });
+
+ bench('JSON.stringify (baseline, no key sorting)', () => {
+ JSON.stringify(obj);
+ });
+
+ bench('JSON.stringify with sorted keys (manual)', () => {
+ const sortedObj = {};
+ Object.keys(obj).sort().forEach(k => {
+ sortedObj[k] = obj[k];
+ });
+ JSON.stringify(sortedObj);
+ });
+});
+
+describe('hash_serializable_object', () => {
+ const small = createFlatObject(5);
+ const medium = createFlatObject(20);
+ const mixed = createMixedObject();
+
+ bench('hash small object', () => {
+ hash_serializable_object(small);
+ });
+
+ bench('hash medium object', () => {
+ hash_serializable_object(medium);
+ });
+
+ bench('hash mixed object', () => {
+ hash_serializable_object(mixed);
+ });
+
+ bench('hash objects with different key orderings (should be equal)', () => {
+ hash_serializable_object(objA);
+ hash_serializable_object(objB);
+ hash_serializable_object(objC);
+ });
+});
diff --git a/src/backend/src/util/opmath.bench.js b/src/backend/src/util/opmath.bench.js
new file mode 100644
index 000000000..c37000c88
--- /dev/null
+++ b/src/backend/src/util/opmath.bench.js
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2024-present Puter Technologies Inc.
+ *
+ * This file is part of Puter.
+ *
+ * Puter is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import { bench, describe } from 'vitest';
+const { EWMA, MovingMode, TimeWindow, normalize } = require('./opmath');
+
+describe('EWMA - Exponential Weighted Moving Average', () => {
+ bench('EWMA put() with constant alpha', () => {
+ const ewma = new EWMA({ initial: 0, alpha: 0.2 });
+ for ( let i = 0; i < 1000; i++ ) {
+ ewma.put(Math.random() * 100);
+ }
+ });
+
+ bench('EWMA put() with function alpha', () => {
+ const ewma = new EWMA({ initial: 0, alpha: () => 0.2 });
+ for ( let i = 0; i < 1000; i++ ) {
+ ewma.put(Math.random() * 100);
+ }
+ });
+
+ bench('EWMA get() after many puts', () => {
+ const ewma = new EWMA({ initial: 0, alpha: 0.2 });
+ for ( let i = 0; i < 100; i++ ) {
+ ewma.put(i);
+ }
+ for ( let i = 0; i < 1000; i++ ) {
+ ewma.get();
+ }
+ });
+});
+
+describe('MovingMode - Mode calculation with sliding window', () => {
+ bench('MovingMode put() with window_size=30', () => {
+ const mode = new MovingMode({ initial: 0, window_size: 30 });
+ for ( let i = 0; i < 1000; i++ ) {
+ mode.put(Math.floor(Math.random() * 10));
+ }
+ });
+
+ bench('MovingMode put() with window_size=100', () => {
+ const mode = new MovingMode({ initial: 0, window_size: 100 });
+ for ( let i = 0; i < 1000; i++ ) {
+ mode.put(Math.floor(Math.random() * 10));
+ }
+ });
+
+ bench('MovingMode with high cardinality values', () => {
+ const mode = new MovingMode({ initial: 0, window_size: 50 });
+ for ( let i = 0; i < 1000; i++ ) {
+ mode.put(Math.floor(Math.random() * 1000));
+ }
+ });
+
+ bench('MovingMode with low cardinality values', () => {
+ const mode = new MovingMode({ initial: 0, window_size: 50 });
+ for ( let i = 0; i < 1000; i++ ) {
+ mode.put(Math.floor(Math.random() * 3));
+ }
+ });
+});
+
+describe('TimeWindow - Time-based sliding window', () => {
+ bench('TimeWindow add() and get()', () => {
+ let fakeTime = 0;
+ const tw = new TimeWindow({
+ window_duration: 1000,
+ reducer: values => values.reduce((a, b) => a + b, 0),
+ now: () => fakeTime,
+ });
+ for ( let i = 0; i < 1000; i++ ) {
+ fakeTime += 10;
+ tw.add(Math.random());
+ }
+ });
+
+ bench('TimeWindow with stale entry removal', () => {
+ let fakeTime = 0;
+ const tw = new TimeWindow({
+ window_duration: 100,
+ reducer: values => values.length,
+ now: () => fakeTime,
+ });
+ for ( let i = 0; i < 1000; i++ ) {
+ fakeTime += 50; // Fast time progression causes stale removal
+ tw.add(i);
+ tw.get();
+ }
+ });
+});
+
+describe('normalize - Exponential normalization', () => {
+ bench('normalize() single value', () => {
+ for ( let i = 0; i < 10000; i++ ) {
+ normalize({ high_value: 0.001 }, Math.random());
+ }
+ });
+
+ bench('normalize() with varying high_value', () => {
+ const high_values = [0.001, 0.01, 0.1, 1, 10];
+ for ( let i = 0; i < 10000; i++ ) {
+ const hv = high_values[i % high_values.length];
+ normalize({ high_value: hv }, Math.random() * 100);
+ }
+ });
+});