From b83f7a5fa4b4fd66d0071ae35f449d771056be6c Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 08:18:46 -0500 Subject: [PATCH 01/22] Fixing performance benchmarks to log results --- benchmarks/performance-observer-benchmark.js | 147 ++++++++++++------- 1 file changed, 96 insertions(+), 51 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 55e0134..d9c75e2 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -6,7 +6,15 @@ class LRUPerformanceProfiler { constructor () { this.entries = []; this.observer = new PerformanceObserver(items => { - items.getEntries().forEach(entry => { + const batch = items.getEntries(); + + // Debug log of raw PerformanceObserver function entries + console.log("PerformanceObserver function batch size:", batch.length); + if (batch.length > 0) { + console.log("PerformanceObserver function entries:", batch); + } + + batch.forEach(entry => { this.entries.push(entry); }); }); @@ -82,6 +90,13 @@ class LRUPerformanceProfiler { console.log("\nšŸ“Š Performance Observer Results"); console.log("================================"); + if (results.length === 0) { + console.log("No function performance entries were recorded by the Performance Observer."); + console.log("Raw PerformanceObserver entries:", this.entries); + + return; + } + console.table(results.map(r => ({ "Function": r.name, "Calls": r.calls, @@ -119,44 +134,31 @@ function generateTestData (size) { async function runPerformanceObserverBenchmarks () { console.log("šŸ”¬ Performance Observer Benchmarks"); console.log("==================================="); + console.log("(Using CustomTimer for function-level timing)"); - const profiler = new LRUPerformanceProfiler(); + const timer = new CustomTimer(); const cacheSize = 1000; const testData = generateTestData(cacheSize * 2); - // Create wrapped functions for different operations - const cache = lru(cacheSize); - - const setOperation = profiler.timerify((key, value) => { - cache.set(key, value); - }, "lru.set"); - - const getOperation = profiler.timerify(key => { - return cache.get(key); - }, "lru.get"); - - const hasOperation = profiler.timerify(key => { - return cache.has(key); - }, "lru.has"); - - const deleteOperation = profiler.timerify(key => { - return cache.delete(key); - }, "lru.delete"); - - const clearOperation = profiler.timerify(() => { - cache.clear(); - }, "lru.clear"); - console.log("Running operations..."); // Phase 1: Fill cache with initial data console.log("Phase 1: Initial cache population"); - for (let i = 0; i < cacheSize; i++) { - setOperation(testData[i].key, testData[i].value); - } + const phase1Cache = lru(cacheSize); + await timer.timeFunction("lru.set (initial population)", () => { + for (let i = 0; i < cacheSize; i++) { + phase1Cache.set(testData[i].key, testData[i].value); + } + }, 1000); // Phase 2: Mixed read/write operations console.log("Phase 2: Mixed operations (realistic workload)"); + const phase2Cache = lru(cacheSize); + // Pre-populate for realistic workload + for (let i = 0; i < cacheSize; i++) { + phase2Cache.set(testData[i].key, testData[i].value); + } + // Deterministic mixed workload without Math.random in the loop const choice = new Uint8Array(5000); const indices = new Uint32Array(5000); @@ -168,38 +170,81 @@ async function runPerformanceObserverBenchmarks () { choice[i] = r % 100; // 0..99 indices[i] = r % testData.length; } - for (let i = 0; i < 5000; i++) { - const pick = choice[i]; - const idx = indices[i]; - if (pick < 60) { - getOperation(testData[idx].key); - } else if (pick < 80) { - setOperation(testData[idx].key, testData[idx].value); - } else if (pick < 95) { - hasOperation(testData[idx].key); - } else { - deleteOperation(testData[idx].key); + + await timer.timeFunction("lru.get (mixed workload)", () => { + for (let i = 0; i < 5000; i++) { + const pick = choice[i]; + if (pick < 60) { + const idx = indices[i]; + phase2Cache.get(testData[idx].key); + } } - } + }, 1000); + + await timer.timeFunction("lru.set (mixed workload)", () => { + for (let i = 0; i < 5000; i++) { + const pick = choice[i]; + if (pick >= 60 && pick < 80) { + const idx = indices[i]; + phase2Cache.set(testData[idx].key, testData[idx].value); + } + } + }, 1000); + + await timer.timeFunction("lru.has (mixed workload)", () => { + for (let i = 0; i < 5000; i++) { + const pick = choice[i]; + if (pick >= 80 && pick < 95) { + const idx = indices[i]; + phase2Cache.has(testData[idx].key); + } + } + }, 1000); + + await timer.timeFunction("lru.delete (mixed workload)", () => { + for (let i = 0; i < 5000; i++) { + const pick = choice[i]; + if (pick >= 95) { + const idx = indices[i]; + phase2Cache.delete(testData[idx].key); + } + } + }, 1000); // Phase 3: Cache eviction stress test console.log("Phase 3: Cache eviction stress test"); - for (let i = 0; i < cacheSize; i++) { - setOperation(`evict_key_${i}`, `evict_value_${i}`); - } + const phase3Cache = lru(cacheSize); + await timer.timeFunction("lru.set (eviction stress)", () => { + for (let i = 0; i < cacheSize; i++) { + phase3Cache.set(`evict_key_${i}`, `evict_value_${i}`); + } + }, 1000); // Phase 4: Some clear operations console.log("Phase 4: Clear operations"); - for (let i = 0; i < 10; i++) { - // Repopulate and clear + await timer.timeFunction("lru.clear", () => { + const cache = lru(cacheSize); for (let j = 0; j < 100; j++) { - setOperation(`temp_${j}`, `temp_value_${j}`); + cache.set(`temp_${j}`, `temp_value_${j}`); } - clearOperation(); - } + cache.clear(); + }, 1000); - profiler.printResults(); - profiler.disconnect(); + // Print results with Performance Observer header + console.log("\nšŸ“Š Performance Observer Results"); + console.log("================================"); + + const results = Array.from(timer.results.values()); + console.table(results.map(r => ({ + "Function": r.name, + "Iterations": r.iterations, + "Avg (ms)": r.avgTime.toFixed(4), + "Min (ms)": r.minTime.toFixed(4), + "Max (ms)": r.maxTime.toFixed(4), + "Median (ms)": r.median.toFixed(4), + "Std Dev": r.stdDev.toFixed(4), + "Ops/sec": Math.round(r.opsPerSec) + }))); } // Custom high-resolution timer benchmark (alternative approach) @@ -381,7 +426,7 @@ async function runAllPerformanceTests () { console.log("\nāœ… Performance tests completed!"); console.log("\nšŸ“‹ Notes:"); - console.log("- Performance Observer: Uses Node.js built-in function timing"); + console.log("- Performance Observer: Uses CustomTimer for function-level timing (PerformanceObserver function entries not supported in this Node.js version)"); console.log("- Custom Timer: High-resolution timing with statistical analysis"); console.log("- Scalability Test: Shows how performance scales with cache size"); From 195d2fee6d4cb446498ce7307d094f3d0b753846 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 08:25:24 -0500 Subject: [PATCH 02/22] Updating performance benchmarks with missing methods, using 10k iterations --- benchmarks/performance-observer-benchmark.js | 49 ++++++++++++++++---- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index d9c75e2..486a4ae 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -149,7 +149,7 @@ async function runPerformanceObserverBenchmarks () { for (let i = 0; i < cacheSize; i++) { phase1Cache.set(testData[i].key, testData[i].value); } - }, 1000); + }, 10000); // Phase 2: Mixed read/write operations console.log("Phase 2: Mixed operations (realistic workload)"); @@ -179,7 +179,7 @@ async function runPerformanceObserverBenchmarks () { phase2Cache.get(testData[idx].key); } } - }, 1000); + }, 10000); await timer.timeFunction("lru.set (mixed workload)", () => { for (let i = 0; i < 5000; i++) { @@ -189,7 +189,7 @@ async function runPerformanceObserverBenchmarks () { phase2Cache.set(testData[idx].key, testData[idx].value); } } - }, 1000); + }, 10000); await timer.timeFunction("lru.has (mixed workload)", () => { for (let i = 0; i < 5000; i++) { @@ -199,7 +199,7 @@ async function runPerformanceObserverBenchmarks () { phase2Cache.has(testData[idx].key); } } - }, 1000); + }, 10000); await timer.timeFunction("lru.delete (mixed workload)", () => { for (let i = 0; i < 5000; i++) { @@ -209,7 +209,7 @@ async function runPerformanceObserverBenchmarks () { phase2Cache.delete(testData[idx].key); } } - }, 1000); + }, 10000); // Phase 3: Cache eviction stress test console.log("Phase 3: Cache eviction stress test"); @@ -335,7 +335,7 @@ async function runCustomTimerBenchmarks () { testData.slice(0, 100).forEach(item => cache.set(item.key, item.value)); // This will cause eviction cache.set("new_key", "new_value"); - }, 1000); + }, 10000); await timer.timeFunction("Cache Hit Get", () => { const item = testData[Math.floor(Math.random() * testData.length)]; @@ -360,13 +360,46 @@ async function runCustomTimerBenchmarks () { testData.slice(0, 500).forEach(item => cache.set(item.key, item.value)); const item = testData[Math.floor(Math.random() * 500)]; cache.delete(item.key); - }, 1000); + }, 10000); await timer.timeFunction("Clear Operation", () => { const cache = lru(cacheSize); testData.slice(0, 500).forEach(item => cache.set(item.key, item.value)); cache.clear(); - }, 1000); + }, 10000); + + // Additional benchmarks for remaining public API methods + await timer.timeFunction("Keys Operation", () => { + readCache.keys(); + }, 10000); + + await timer.timeFunction("Values Operation", () => { + readCache.values(); + }, 10000); + + await timer.timeFunction("Entries Operation", () => { + readCache.entries(); + }, 10000); + + await timer.timeFunction("Evict Operation", () => { + const cache = lru(100); + testData.slice(0, 100).forEach(item => cache.set(item.key, item.value)); + cache.evict(); + }, 10000); + + await timer.timeFunction("SetWithEvicted Operation", () => { + const cache = lru(2); + cache.set(testData[0].key, testData[0].value); + cache.set(testData[1].key, testData[1].value); + cache.setWithEvicted("extra_key", "extra_value"); + }, 10000); + + await timer.timeFunction("ExpiresAt Operation", () => { + const cache = lru(cacheSize, 5000); + const item = testData[Math.floor(Math.random() * testData.length)]; + cache.set(item.key, item.value); + cache.expiresAt(item.key); + }, 10000); timer.printResults(); } From 2378abf74cf95ff5d8134b1bb1e506c3faa26b27 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 08:27:45 -0500 Subject: [PATCH 03/22] Removing unneeded class from performance benchmarks --- benchmarks/performance-observer-benchmark.js | 120 +------------------ 1 file changed, 1 insertion(+), 119 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 486a4ae..b4b09bc 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -1,123 +1,6 @@ -import { performance, PerformanceObserver } from "node:perf_hooks"; +import { performance } from "node:perf_hooks"; import { lru } from "../dist/tiny-lru.js"; -// Performance observer for function timing -class LRUPerformanceProfiler { - constructor () { - this.entries = []; - this.observer = new PerformanceObserver(items => { - const batch = items.getEntries(); - - // Debug log of raw PerformanceObserver function entries - console.log("PerformanceObserver function batch size:", batch.length); - if (batch.length > 0) { - console.log("PerformanceObserver function entries:", batch); - } - - batch.forEach(entry => { - this.entries.push(entry); - }); - }); - this.observer.observe({ entryTypes: ["function"] }); - } - - timerify (fn, name) { - const wrappedFn = performance.timerify(fn); - // Override the name for better reporting (safely handle non-configurable name property) - try { - Object.defineProperty(wrappedFn, "name", { value: name, configurable: true }); - } catch (error) { // eslint-disable-line no-unused-vars - // If we can't redefine the name property, create a wrapper with the desired name - const namedWrapper = { - [name]: (...args) => wrappedFn(...args) - }[name]; - - return namedWrapper; - } - - return wrappedFn; - } - - getResults () { - const results = new Map(); - - this.entries.forEach(entry => { - if (!results.has(entry.name)) { - results.set(entry.name, { - name: entry.name, - calls: 0, - totalTime: 0, - minTime: Infinity, - maxTime: 0, - times: [] - }); - } - - const result = results.get(entry.name); - result.calls++; - result.totalTime += entry.duration; - result.minTime = Math.min(result.minTime, entry.duration); - result.maxTime = Math.max(result.maxTime, entry.duration); - result.times.push(entry.duration); - }); - - // Calculate statistics - results.forEach(result => { - result.avgTime = result.totalTime / result.calls; - - // Calculate standard deviation - const variance = result.times.reduce((acc, time) => { - return acc + Math.pow(time - result.avgTime, 2); - }, 0) / result.calls; - result.stdDev = Math.sqrt(variance); - - // Calculate median - const sorted = [...result.times].sort((a, b) => a - b); - const mid = Math.floor(sorted.length / 2); - result.median = sorted.length % 2 === 0 ? - (sorted[mid - 1] + sorted[mid]) / 2 : - sorted[mid]; - - // Operations per second (rough estimate) - result.opsPerSec = result.calls / (result.totalTime / 1000); // duration is in ms - }); - - return Array.from(results.values()); - } - - printResults () { - const results = this.getResults(); - console.log("\nšŸ“Š Performance Observer Results"); - console.log("================================"); - - if (results.length === 0) { - console.log("No function performance entries were recorded by the Performance Observer."); - console.log("Raw PerformanceObserver entries:", this.entries); - - return; - } - - console.table(results.map(r => ({ - "Function": r.name, - "Calls": r.calls, - "Avg (ms)": r.avgTime.toFixed(4), - "Min (ms)": r.minTime.toFixed(4), - "Max (ms)": r.maxTime.toFixed(4), - "Median (ms)": r.median.toFixed(4), - "Std Dev": r.stdDev.toFixed(4), - "Ops/sec": Math.round(r.opsPerSec) - }))); - } - - disconnect () { - this.observer.disconnect(); - } - - reset () { - this.entries = []; - } -} - // Test data generation function generateTestData (size) { const out = new Array(size); @@ -479,6 +362,5 @@ export { runPerformanceObserverBenchmarks, runCustomTimerBenchmarks, runScalabilityTest, - LRUPerformanceProfiler, CustomTimer }; From c4cd98daab3d3ad1c89c0cfacf7ed6343a0ea9a3 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 08:30:25 -0500 Subject: [PATCH 04/22] Fixing 'test' script & benchmark file --- benchmarks/performance-observer-benchmark.js | 132 +++++++++---------- package.json | 2 +- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index b4b09bc..c5f1af0 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -1,6 +1,70 @@ import { performance } from "node:perf_hooks"; import { lru } from "../dist/tiny-lru.js"; +// Custom high-resolution timer benchmark (alternative approach) +class CustomTimer { + constructor () { + this.results = new Map(); + } + + async timeFunction (name, fn, iterations = 1000) { + const times = []; + + // Warmup + for (let i = 0; i < Math.min(100, iterations / 10); i++) { + await fn(); + } + + // Actual measurement + for (let i = 0; i < iterations; i++) { + const start = performance.now(); + await fn(); + const end = performance.now(); + times.push(end - start); + } + + // Calculate statistics + const totalTime = times.reduce((a, b) => a + b, 0); + const avgTime = totalTime / iterations; + const minTime = Math.min(...times); + const maxTime = Math.max(...times); + + const sorted = [...times].sort((a, b) => a - b); + const median = sorted[Math.floor(sorted.length / 2)]; + + const variance = times.reduce((acc, time) => acc + Math.pow(time - avgTime, 2), 0) / iterations; + const stdDev = Math.sqrt(variance); + + this.results.set(name, { + name, + iterations, + avgTime, + minTime, + maxTime, + median, + stdDev, + opsPerSec: 1000 / avgTime // Convert ms to ops/sec + }); + } + + printResults () { + console.log("\nā±ļø Custom Timer Results"); + console.log("========================"); + + const results = Array.from(this.results.values()); + console.table(results.map(r => ({ + "Operation": r.name, + "Iterations": r.iterations, + "Avg (ms)": r.avgTime.toFixed(6), + "Min (ms)": r.minTime.toFixed(6), + "Max (ms)": r.maxTime.toFixed(6), + "Median (ms)": r.median.toFixed(6), + "Std Dev": r.stdDev.toFixed(6), + "Ops/sec": Math.round(r.opsPerSec) + }))); + } +} + // Test data generation function generateTestData (size) { const out = new Array(size); @@ -101,7 +165,7 @@ async function runPerformanceObserverBenchmarks () { for (let i = 0; i < cacheSize; i++) { phase3Cache.set(`evict_key_${i}`, `evict_value_${i}`); } - }, 1000); + }, 10000); // Phase 4: Some clear operations console.log("Phase 4: Clear operations"); @@ -111,7 +175,7 @@ async function runPerformanceObserverBenchmarks () { cache.set(`temp_${j}`, `temp_value_${j}`); } cache.clear(); - }, 1000); + }, 10000); // Print results with Performance Observer header console.log("\nšŸ“Š Performance Observer Results"); @@ -130,70 +194,6 @@ async function runPerformanceObserverBenchmarks () { }))); } -// Custom high-resolution timer benchmark (alternative approach) -class CustomTimer { - constructor () { - this.results = new Map(); - } - - async timeFunction (name, fn, iterations = 1000) { - const times = []; - - // Warmup - for (let i = 0; i < Math.min(100, iterations / 10); i++) { - await fn(); - } - - // Actual measurement - for (let i = 0; i < iterations; i++) { - const start = performance.now(); - await fn(); - const end = performance.now(); - times.push(end - start); - } - - // Calculate statistics - const totalTime = times.reduce((a, b) => a + b, 0); - const avgTime = totalTime / iterations; - const minTime = Math.min(...times); - const maxTime = Math.max(...times); - - const sorted = [...times].sort((a, b) => a - b); - const median = sorted[Math.floor(sorted.length / 2)]; - - const variance = times.reduce((acc, time) => acc + Math.pow(time - avgTime, 2), 0) / iterations; - const stdDev = Math.sqrt(variance); - - this.results.set(name, { - name, - iterations, - avgTime, - minTime, - maxTime, - median, - stdDev, - opsPerSec: 1000 / avgTime // Convert ms to ops/sec - }); - } - - printResults () { - console.log("\nā±ļø Custom Timer Results"); - console.log("========================"); - - const results = Array.from(this.results.values()); - console.table(results.map(r => ({ - "Operation": r.name, - "Iterations": r.iterations, - "Avg (ms)": r.avgTime.toFixed(6), - "Min (ms)": r.minTime.toFixed(6), - "Max (ms)": r.maxTime.toFixed(6), - "Median (ms)": r.median.toFixed(6), - "Std Dev": r.stdDev.toFixed(6), - "Ops/sec": Math.round(r.opsPerSec) - }))); - } -} - async function runCustomTimerBenchmarks () { console.log("\n⚔ Custom Timer Benchmarks"); console.log("=========================="); diff --git a/package.json b/package.json index 945be4b..f1aef0c 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "lint": "eslint --fix *.js src/*.js tests/**/*.js benchmarks/*.js", "mocha": "c8 mocha \"tests/**/*.js\"", "rollup": "rollup --config", - "test": "npm run mocha", + "test": "npm run lint && npm run mocha", "prepare": "husky" }, "devDependencies": { From 2bf0bfbf119912924fda7dd99168d64b09239564 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 08:42:32 -0500 Subject: [PATCH 05/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 66 +++++++++++--------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index c5f1af0..8951add 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -92,10 +92,11 @@ async function runPerformanceObserverBenchmarks () { // Phase 1: Fill cache with initial data console.log("Phase 1: Initial cache population"); const phase1Cache = lru(cacheSize); + let phase1Index = 0; await timer.timeFunction("lru.set (initial population)", () => { - for (let i = 0; i < cacheSize; i++) { - phase1Cache.set(testData[i].key, testData[i].value); - } + const i = phase1Index % cacheSize; + phase1Cache.set(testData[i].key, testData[i].value); + phase1Index++; }, 10000); // Phase 2: Mixed read/write operations @@ -118,53 +119,58 @@ async function runPerformanceObserverBenchmarks () { indices[i] = r % testData.length; } + let mixedGetIndex = 0; await timer.timeFunction("lru.get (mixed workload)", () => { - for (let i = 0; i < 5000; i++) { - const pick = choice[i]; - if (pick < 60) { - const idx = indices[i]; - phase2Cache.get(testData[idx].key); - } + const i = mixedGetIndex % choice.length; + const pick = choice[i]; + if (pick < 60) { + const idx = indices[i]; + phase2Cache.get(testData[idx].key); } + mixedGetIndex++; }, 10000); + let mixedSetIndex = 0; await timer.timeFunction("lru.set (mixed workload)", () => { - for (let i = 0; i < 5000; i++) { - const pick = choice[i]; - if (pick >= 60 && pick < 80) { - const idx = indices[i]; - phase2Cache.set(testData[idx].key, testData[idx].value); - } + const i = mixedSetIndex % choice.length; + const pick = choice[i]; + if (pick >= 60 && pick < 80) { + const idx = indices[i]; + phase2Cache.set(testData[idx].key, testData[idx].value); } + mixedSetIndex++; }, 10000); + let mixedHasIndex = 0; await timer.timeFunction("lru.has (mixed workload)", () => { - for (let i = 0; i < 5000; i++) { - const pick = choice[i]; - if (pick >= 80 && pick < 95) { - const idx = indices[i]; - phase2Cache.has(testData[idx].key); - } + const i = mixedHasIndex % choice.length; + const pick = choice[i]; + if (pick >= 80 && pick < 95) { + const idx = indices[i]; + phase2Cache.has(testData[idx].key); } + mixedHasIndex++; }, 10000); + let mixedDeleteIndex = 0; await timer.timeFunction("lru.delete (mixed workload)", () => { - for (let i = 0; i < 5000; i++) { - const pick = choice[i]; - if (pick >= 95) { - const idx = indices[i]; - phase2Cache.delete(testData[idx].key); - } + const i = mixedDeleteIndex % choice.length; + const pick = choice[i]; + if (pick >= 95) { + const idx = indices[i]; + phase2Cache.delete(testData[idx].key); } + mixedDeleteIndex++; }, 10000); // Phase 3: Cache eviction stress test console.log("Phase 3: Cache eviction stress test"); const phase3Cache = lru(cacheSize); + let phase3Index = 0; await timer.timeFunction("lru.set (eviction stress)", () => { - for (let i = 0; i < cacheSize; i++) { - phase3Cache.set(`evict_key_${i}`, `evict_value_${i}`); - } + const i = phase3Index; + phase3Cache.set(`evict_key_${i}`, `evict_value_${i}`); + phase3Index++; }, 10000); // Phase 4: Some clear operations From cb4304af0119071f1d4cebbdd523ac0cb162c75c Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 08:46:59 -0500 Subject: [PATCH 06/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 159 ++++++++++--------- 1 file changed, 87 insertions(+), 72 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 8951add..6018444 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -206,89 +206,104 @@ async function runCustomTimerBenchmarks () { const timer = new CustomTimer(); const cacheSize = 1000; - const testData = generateTestData(cacheSize); + const iterations = 10000; + const testData = generateTestData(cacheSize * 2); - // Pre-populate cache for read tests - const readCache = lru(cacheSize); - testData.forEach(item => readCache.set(item.key, item.value)); + console.log("Running operations..."); - // Benchmark different operations - await timer.timeFunction("Empty Cache Set", () => { - const cache = lru(cacheSize); - const item = testData[Math.floor(Math.random() * testData.length)]; - cache.set(item.key, item.value); - }, 10000); + // Phase 1: Fill cache with initial data + console.log("Phase 1: Initial cache population"); + const phase1Cache = lru(cacheSize); + let phase1Index = 0; + await timer.timeFunction("lru.set (initial population)", () => { + const i = phase1Index % cacheSize; + phase1Cache.set(testData[i].key, testData[i].value); + phase1Index++; + }, iterations); - await timer.timeFunction("Full Cache Set (eviction)", () => { - const cache = lru(100); // Smaller cache for guaranteed eviction - testData.slice(0, 100).forEach(item => cache.set(item.key, item.value)); - // This will cause eviction - cache.set("new_key", "new_value"); - }, 10000); + // Phase 2: Mixed read/write operations + console.log("Phase 2: Mixed operations (realistic workload)"); + const phase2Cache = lru(cacheSize); + // Pre-populate for realistic workload + for (let i = 0; i < cacheSize; i++) { + phase2Cache.set(testData[i].key, testData[i].value); + } - await timer.timeFunction("Cache Hit Get", () => { - const item = testData[Math.floor(Math.random() * testData.length)]; - readCache.get(item.key); - }, 10000); + // Deterministic mixed workload without Math.random in the loop + const choice = new Uint8Array(5000); + const indices = new Uint32Array(5000); + let a = 1103515245, c = 12345, m = 2 ** 31; + let seed = 42; + for (let i = 0; i < 5000; i++) { + seed = (a * seed + c) % m; + const r = seed >>> 0; + choice[i] = r % 100; // 0..99 + indices[i] = r % testData.length; + } - await timer.timeFunction("Cache Miss Get", () => { - readCache.get(`nonexistent_${Math.random()}`); - }, 10000); + let mixedGetIndex = 0; + await timer.timeFunction("lru.get (mixed workload)", () => { + const i = mixedGetIndex % choice.length; + const pick = choice[i]; + if (pick < 60) { + const idx = indices[i]; + phase2Cache.get(testData[idx].key); + } + mixedGetIndex++; + }, iterations); - await timer.timeFunction("Has Operation (hit)", () => { - const item = testData[Math.floor(Math.random() * testData.length)]; - readCache.has(item.key); - }, 10000); + let mixedSetIndex = 0; + await timer.timeFunction("lru.set (mixed workload)", () => { + const i = mixedSetIndex % choice.length; + const pick = choice[i]; + if (pick >= 60 && pick < 80) { + const idx = indices[i]; + phase2Cache.set(testData[idx].key, testData[idx].value); + } + mixedSetIndex++; + }, iterations); - await timer.timeFunction("Has Operation (miss)", () => { - readCache.has(`nonexistent_${Math.random()}`); - }, 10000); + let mixedHasIndex = 0; + await timer.timeFunction("lru.has (mixed workload)", () => { + const i = mixedHasIndex % choice.length; + const pick = choice[i]; + if (pick >= 80 && pick < 95) { + const idx = indices[i]; + phase2Cache.has(testData[idx].key); + } + mixedHasIndex++; + }, iterations); - await timer.timeFunction("Delete Operation", () => { - const cache = lru(cacheSize); - testData.slice(0, 500).forEach(item => cache.set(item.key, item.value)); - const item = testData[Math.floor(Math.random() * 500)]; - cache.delete(item.key); - }, 10000); + let mixedDeleteIndex = 0; + await timer.timeFunction("lru.delete (mixed workload)", () => { + const i = mixedDeleteIndex % choice.length; + const pick = choice[i]; + if (pick >= 95) { + const idx = indices[i]; + phase2Cache.delete(testData[idx].key); + } + mixedDeleteIndex++; + }, iterations); + + // Phase 3: Cache eviction stress test + console.log("Phase 3: Cache eviction stress test"); + const phase3Cache = lru(cacheSize); + let phase3Index = 0; + await timer.timeFunction("lru.set (eviction stress)", () => { + const i = phase3Index; + phase3Cache.set(`evict_key_${i}`, `evict_value_${i}`); + phase3Index++; + }, iterations); - await timer.timeFunction("Clear Operation", () => { + // Phase 4: Some clear operations + console.log("Phase 4: Clear operations"); + await timer.timeFunction("lru.clear", () => { const cache = lru(cacheSize); - testData.slice(0, 500).forEach(item => cache.set(item.key, item.value)); + for (let j = 0; j < 100; j++) { + cache.set(`temp_${j}`, `temp_value_${j}`); + } cache.clear(); - }, 10000); - - // Additional benchmarks for remaining public API methods - await timer.timeFunction("Keys Operation", () => { - readCache.keys(); - }, 10000); - - await timer.timeFunction("Values Operation", () => { - readCache.values(); - }, 10000); - - await timer.timeFunction("Entries Operation", () => { - readCache.entries(); - }, 10000); - - await timer.timeFunction("Evict Operation", () => { - const cache = lru(100); - testData.slice(0, 100).forEach(item => cache.set(item.key, item.value)); - cache.evict(); - }, 10000); - - await timer.timeFunction("SetWithEvicted Operation", () => { - const cache = lru(2); - cache.set(testData[0].key, testData[0].value); - cache.set(testData[1].key, testData[1].value); - cache.setWithEvicted("extra_key", "extra_value"); - }, 10000); - - await timer.timeFunction("ExpiresAt Operation", () => { - const cache = lru(cacheSize, 5000); - const item = testData[Math.floor(Math.random() * testData.length)]; - cache.set(item.key, item.value); - cache.expiresAt(item.key); - }, 10000); + }, iterations); timer.printResults(); } From 5e0da754bb8fb9ce3f753244108cd50330b08e37 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 08:52:15 -0500 Subject: [PATCH 07/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 6018444..086b68d 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -321,8 +321,8 @@ async function runScalabilityTest () { const testData = generateTestData(size); // Test set performance - const setStart = performance.now(); const cache = lru(size); + const setStart = performance.now(); testData.forEach(item => cache.set(item.key, item.value)); const setEnd = performance.now(); const setTime = setEnd - setStart; From 83910f88468b8bd9a1897edf3035b368e0b8ee9d Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 08:58:02 -0500 Subject: [PATCH 08/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 117 +++++++------------ 1 file changed, 45 insertions(+), 72 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 086b68d..bcdcb94 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -107,61 +107,48 @@ async function runPerformanceObserverBenchmarks () { phase2Cache.set(testData[i].key, testData[i].value); } - // Deterministic mixed workload without Math.random in the loop - const choice = new Uint8Array(5000); - const indices = new Uint32Array(5000); - let a = 1103515245, c = 12345, m = 2 ** 31; - let seed = 42; - for (let i = 0; i < 5000; i++) { - seed = (a * seed + c) % m; - const r = seed >>> 0; - choice[i] = r % 100; // 0..99 - indices[i] = r % testData.length; + // Deterministic mixed workload that exercises the entire cache without conditionals + const phase2Iterations = 10000; + const getIndices = new Uint32Array(phase2Iterations); + const setIndices = new Uint32Array(phase2Iterations); + const hasIndices = new Uint32Array(phase2Iterations); + const deleteIndices = new Uint32Array(phase2Iterations); + + for (let i = 0; i < phase2Iterations; i++) { + const idx = i % cacheSize; + getIndices[i] = idx; + setIndices[i] = idx; + hasIndices[i] = idx; + deleteIndices[i] = idx; } let mixedGetIndex = 0; await timer.timeFunction("lru.get (mixed workload)", () => { - const i = mixedGetIndex % choice.length; - const pick = choice[i]; - if (pick < 60) { - const idx = indices[i]; - phase2Cache.get(testData[idx].key); - } + const idx = getIndices[mixedGetIndex % phase2Iterations]; + phase2Cache.get(testData[idx].key); mixedGetIndex++; - }, 10000); + }, phase2Iterations); let mixedSetIndex = 0; await timer.timeFunction("lru.set (mixed workload)", () => { - const i = mixedSetIndex % choice.length; - const pick = choice[i]; - if (pick >= 60 && pick < 80) { - const idx = indices[i]; - phase2Cache.set(testData[idx].key, testData[idx].value); - } + const idx = setIndices[mixedSetIndex % phase2Iterations]; + phase2Cache.set(testData[idx].key, testData[idx].value); mixedSetIndex++; - }, 10000); + }, phase2Iterations); let mixedHasIndex = 0; await timer.timeFunction("lru.has (mixed workload)", () => { - const i = mixedHasIndex % choice.length; - const pick = choice[i]; - if (pick >= 80 && pick < 95) { - const idx = indices[i]; - phase2Cache.has(testData[idx].key); - } + const idx = hasIndices[mixedHasIndex % phase2Iterations]; + phase2Cache.has(testData[idx].key); mixedHasIndex++; - }, 10000); + }, phase2Iterations); let mixedDeleteIndex = 0; await timer.timeFunction("lru.delete (mixed workload)", () => { - const i = mixedDeleteIndex % choice.length; - const pick = choice[i]; - if (pick >= 95) { - const idx = indices[i]; - phase2Cache.delete(testData[idx].key); - } + const idx = deleteIndices[mixedDeleteIndex % phase2Iterations]; + phase2Cache.delete(testData[idx].key); mixedDeleteIndex++; - }, 10000); + }, phase2Iterations); // Phase 3: Cache eviction stress test console.log("Phase 3: Cache eviction stress test"); @@ -229,59 +216,45 @@ async function runCustomTimerBenchmarks () { phase2Cache.set(testData[i].key, testData[i].value); } - // Deterministic mixed workload without Math.random in the loop - const choice = new Uint8Array(5000); - const indices = new Uint32Array(5000); - let a = 1103515245, c = 12345, m = 2 ** 31; - let seed = 42; - for (let i = 0; i < 5000; i++) { - seed = (a * seed + c) % m; - const r = seed >>> 0; - choice[i] = r % 100; // 0..99 - indices[i] = r % testData.length; + // Deterministic mixed workload that exercises the entire cache without conditionals + const getIndices = new Uint32Array(iterations); + const setIndices = new Uint32Array(iterations); + const hasIndices = new Uint32Array(iterations); + const deleteIndices = new Uint32Array(iterations); + + for (let i = 0; i < iterations; i++) { + const idx = i % cacheSize; + getIndices[i] = idx; + setIndices[i] = idx; + hasIndices[i] = idx; + deleteIndices[i] = idx; } let mixedGetIndex = 0; await timer.timeFunction("lru.get (mixed workload)", () => { - const i = mixedGetIndex % choice.length; - const pick = choice[i]; - if (pick < 60) { - const idx = indices[i]; - phase2Cache.get(testData[idx].key); - } + const idx = getIndices[mixedGetIndex % iterations]; + phase2Cache.get(testData[idx].key); mixedGetIndex++; }, iterations); let mixedSetIndex = 0; await timer.timeFunction("lru.set (mixed workload)", () => { - const i = mixedSetIndex % choice.length; - const pick = choice[i]; - if (pick >= 60 && pick < 80) { - const idx = indices[i]; - phase2Cache.set(testData[idx].key, testData[idx].value); - } + const idx = setIndices[mixedSetIndex % iterations]; + phase2Cache.set(testData[idx].key, testData[idx].value); mixedSetIndex++; }, iterations); let mixedHasIndex = 0; await timer.timeFunction("lru.has (mixed workload)", () => { - const i = mixedHasIndex % choice.length; - const pick = choice[i]; - if (pick >= 80 && pick < 95) { - const idx = indices[i]; - phase2Cache.has(testData[idx].key); - } + const idx = hasIndices[mixedHasIndex % iterations]; + phase2Cache.has(testData[idx].key); mixedHasIndex++; }, iterations); let mixedDeleteIndex = 0; await timer.timeFunction("lru.delete (mixed workload)", () => { - const i = mixedDeleteIndex % choice.length; - const pick = choice[i]; - if (pick >= 95) { - const idx = indices[i]; - phase2Cache.delete(testData[idx].key); - } + const idx = deleteIndices[mixedDeleteIndex % iterations]; + phase2Cache.delete(testData[idx].key); mixedDeleteIndex++; }, iterations); From 48c4d91b986124844f77d8fab85e876b43ceb83c Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 08:59:27 -0500 Subject: [PATCH 09/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index bcdcb94..28a8ece 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -162,11 +162,12 @@ async function runPerformanceObserverBenchmarks () { // Phase 4: Some clear operations console.log("Phase 4: Clear operations"); + const cache = lru(cacheSize); + for (let j = 0; j < 100; j++) { + cache.set(`temp_${j}`, `temp_value_${j}`); + } + await timer.timeFunction("lru.clear", () => { - const cache = lru(cacheSize); - for (let j = 0; j < 100; j++) { - cache.set(`temp_${j}`, `temp_value_${j}`); - } cache.clear(); }, 10000); From 2fbf290ed9a7aba01334195ec6def90a7c6e812c Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 09:01:23 -0500 Subject: [PATCH 10/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 28a8ece..3588a3e 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -162,13 +162,13 @@ async function runPerformanceObserverBenchmarks () { // Phase 4: Some clear operations console.log("Phase 4: Clear operations"); - const cache = lru(cacheSize); + const phase4Cache = lru(cacheSize); for (let j = 0; j < 100; j++) { - cache.set(`temp_${j}`, `temp_value_${j}`); + phase4Cache.set(`temp_${j}`, `temp_value_${j}`); } await timer.timeFunction("lru.clear", () => { - cache.clear(); + phase4Cache.clear(); }, 10000); // Print results with Performance Observer header From 22cb59388aa7d1e977dac97b3481b1c4de138e7b Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 09:09:07 -0500 Subject: [PATCH 11/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 3588a3e..b7cd391 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -152,12 +152,11 @@ async function runPerformanceObserverBenchmarks () { // Phase 3: Cache eviction stress test console.log("Phase 3: Cache eviction stress test"); - const phase3Cache = lru(cacheSize); - let phase3Index = 0; + const phase3Cache = lru(2); + let phase3Index = 1; + phase3Cache.set(`evict_key_${phase3Index}`, `evict__value_${phase3Index++}`); await timer.timeFunction("lru.set (eviction stress)", () => { - const i = phase3Index; - phase3Cache.set(`evict_key_${i}`, `evict_value_${i}`); - phase3Index++; + phase3Cache.set(`evict_key_${phase3Index}`, `evict_value_${phase3Index++}`); }, 10000); // Phase 4: Some clear operations From 54bafe9985524bd96a49d36cb55d0add3845eb4c Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 09:12:44 -0500 Subject: [PATCH 12/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index b7cd391..d17b18e 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -161,12 +161,11 @@ async function runPerformanceObserverBenchmarks () { // Phase 4: Some clear operations console.log("Phase 4: Clear operations"); - const phase4Cache = lru(cacheSize); - for (let j = 0; j < 100; j++) { - phase4Cache.set(`temp_${j}`, `temp_value_${j}`); - } - await timer.timeFunction("lru.clear", () => { + const phase4Cache = lru(cacheSize); + for (let j = 0; j < 100; j++) { + phase4Cache.set(`temp_${j}`, `temp_value_${j}`); + } phase4Cache.clear(); }, 10000); From 955f5bd30ac25548d43100b3fd1bcfe05ccf77f6 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 09:13:47 -0500 Subject: [PATCH 13/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index d17b18e..73b1f2d 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -108,7 +108,7 @@ async function runPerformanceObserverBenchmarks () { } // Deterministic mixed workload that exercises the entire cache without conditionals - const phase2Iterations = 10000; + const phase2Iterations = 1000; const getIndices = new Uint32Array(phase2Iterations); const setIndices = new Uint32Array(phase2Iterations); const hasIndices = new Uint32Array(phase2Iterations); From ec5db24f2049f206d4b97f39d2fd700736a09233 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 09:19:11 -0500 Subject: [PATCH 14/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 40 ++++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 73b1f2d..1977b4c 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -86,6 +86,7 @@ async function runPerformanceObserverBenchmarks () { const timer = new CustomTimer(); const cacheSize = 1000; const testData = generateTestData(cacheSize * 2); + const iterations = 10000; console.log("Running operations..."); @@ -97,7 +98,7 @@ async function runPerformanceObserverBenchmarks () { const i = phase1Index % cacheSize; phase1Cache.set(testData[i].key, testData[i].value); phase1Index++; - }, 10000); + }, iterations); // Phase 2: Mixed read/write operations console.log("Phase 2: Mixed operations (realistic workload)"); @@ -108,13 +109,12 @@ async function runPerformanceObserverBenchmarks () { } // Deterministic mixed workload that exercises the entire cache without conditionals - const phase2Iterations = 1000; - const getIndices = new Uint32Array(phase2Iterations); - const setIndices = new Uint32Array(phase2Iterations); - const hasIndices = new Uint32Array(phase2Iterations); - const deleteIndices = new Uint32Array(phase2Iterations); + const getIndices = new Uint32Array(iterations); + const setIndices = new Uint32Array(iterations); + const hasIndices = new Uint32Array(iterations); + const deleteIndices = new Uint32Array(iterations); - for (let i = 0; i < phase2Iterations; i++) { + for (let i = 0; i < iterations; i++) { const idx = i % cacheSize; getIndices[i] = idx; setIndices[i] = idx; @@ -124,31 +124,31 @@ async function runPerformanceObserverBenchmarks () { let mixedGetIndex = 0; await timer.timeFunction("lru.get (mixed workload)", () => { - const idx = getIndices[mixedGetIndex % phase2Iterations]; + const idx = getIndices[mixedGetIndex % iterations]; phase2Cache.get(testData[idx].key); mixedGetIndex++; - }, phase2Iterations); + }, iterations); let mixedSetIndex = 0; await timer.timeFunction("lru.set (mixed workload)", () => { - const idx = setIndices[mixedSetIndex % phase2Iterations]; + const idx = setIndices[mixedSetIndex % iterations]; phase2Cache.set(testData[idx].key, testData[idx].value); mixedSetIndex++; - }, phase2Iterations); + }, iterations); let mixedHasIndex = 0; await timer.timeFunction("lru.has (mixed workload)", () => { - const idx = hasIndices[mixedHasIndex % phase2Iterations]; + const idx = hasIndices[mixedHasIndex % iterations]; phase2Cache.has(testData[idx].key); mixedHasIndex++; - }, phase2Iterations); + }, iterations); let mixedDeleteIndex = 0; await timer.timeFunction("lru.delete (mixed workload)", () => { - const idx = deleteIndices[mixedDeleteIndex % phase2Iterations]; + const idx = deleteIndices[mixedDeleteIndex % iterations]; phase2Cache.delete(testData[idx].key); mixedDeleteIndex++; - }, phase2Iterations); + }, 1000); // Phase 3: Cache eviction stress test console.log("Phase 3: Cache eviction stress test"); @@ -157,17 +157,15 @@ async function runPerformanceObserverBenchmarks () { phase3Cache.set(`evict_key_${phase3Index}`, `evict__value_${phase3Index++}`); await timer.timeFunction("lru.set (eviction stress)", () => { phase3Cache.set(`evict_key_${phase3Index}`, `evict_value_${phase3Index++}`); - }, 10000); + }, iterations); // Phase 4: Some clear operations console.log("Phase 4: Clear operations"); await timer.timeFunction("lru.clear", () => { - const phase4Cache = lru(cacheSize); - for (let j = 0; j < 100; j++) { - phase4Cache.set(`temp_${j}`, `temp_value_${j}`); - } + const phase4Cache = lru(1); + phase4Cache.set("temp_1", "temp_value_1"); phase4Cache.clear(); - }, 10000); + }, iterations); // Print results with Performance Observer header console.log("\nšŸ“Š Performance Observer Results"); From f61e734e8c88610b7418cb4a19f1726fc6b8a035 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 09:20:08 -0500 Subject: [PATCH 15/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 1977b4c..705c247 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -161,8 +161,8 @@ async function runPerformanceObserverBenchmarks () { // Phase 4: Some clear operations console.log("Phase 4: Clear operations"); + const phase4Cache = lru(1); await timer.timeFunction("lru.clear", () => { - const phase4Cache = lru(1); phase4Cache.set("temp_1", "temp_value_1"); phase4Cache.clear(); }, iterations); From 2b8e697380d06c3cbad1a44b67d75bd42929eb22 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 09:27:50 -0500 Subject: [PATCH 16/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 58 ++++++++++++++++---- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 705c247..5dfa55e 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -101,7 +101,7 @@ async function runPerformanceObserverBenchmarks () { }, iterations); // Phase 2: Mixed read/write operations - console.log("Phase 2: Mixed operations (realistic workload)"); + console.log("Phase 2: Mixed operations"); const phase2Cache = lru(cacheSize); // Pre-populate for realistic workload for (let i = 0; i < cacheSize; i++) { @@ -123,28 +123,28 @@ async function runPerformanceObserverBenchmarks () { } let mixedGetIndex = 0; - await timer.timeFunction("lru.get (mixed workload)", () => { + await timer.timeFunction("lru.get", () => { const idx = getIndices[mixedGetIndex % iterations]; phase2Cache.get(testData[idx].key); mixedGetIndex++; }, iterations); let mixedSetIndex = 0; - await timer.timeFunction("lru.set (mixed workload)", () => { + await timer.timeFunction("lru.set", () => { const idx = setIndices[mixedSetIndex % iterations]; phase2Cache.set(testData[idx].key, testData[idx].value); mixedSetIndex++; }, iterations); let mixedHasIndex = 0; - await timer.timeFunction("lru.has (mixed workload)", () => { + await timer.timeFunction("lru.has", () => { const idx = hasIndices[mixedHasIndex % iterations]; phase2Cache.has(testData[idx].key); mixedHasIndex++; }, iterations); let mixedDeleteIndex = 0; - await timer.timeFunction("lru.delete (mixed workload)", () => { + await timer.timeFunction("lru.delete", () => { const idx = deleteIndices[mixedDeleteIndex % iterations]; phase2Cache.delete(testData[idx].key); mixedDeleteIndex++; @@ -206,7 +206,7 @@ async function runCustomTimerBenchmarks () { }, iterations); // Phase 2: Mixed read/write operations - console.log("Phase 2: Mixed operations (realistic workload)"); + console.log("Phase 2: Mixed operations"); const phase2Cache = lru(cacheSize); // Pre-populate for realistic workload for (let i = 0; i < cacheSize; i++) { @@ -228,28 +228,28 @@ async function runCustomTimerBenchmarks () { } let mixedGetIndex = 0; - await timer.timeFunction("lru.get (mixed workload)", () => { + await timer.timeFunction("lru.get", () => { const idx = getIndices[mixedGetIndex % iterations]; phase2Cache.get(testData[idx].key); mixedGetIndex++; }, iterations); let mixedSetIndex = 0; - await timer.timeFunction("lru.set (mixed workload)", () => { + await timer.timeFunction("lru.set", () => { const idx = setIndices[mixedSetIndex % iterations]; phase2Cache.set(testData[idx].key, testData[idx].value); mixedSetIndex++; }, iterations); let mixedHasIndex = 0; - await timer.timeFunction("lru.has (mixed workload)", () => { + await timer.timeFunction("lru.has", () => { const idx = hasIndices[mixedHasIndex % iterations]; phase2Cache.has(testData[idx].key); mixedHasIndex++; }, iterations); let mixedDeleteIndex = 0; - await timer.timeFunction("lru.delete (mixed workload)", () => { + await timer.timeFunction("lru.delete", () => { const idx = deleteIndices[mixedDeleteIndex % iterations]; phase2Cache.delete(testData[idx].key); mixedDeleteIndex++; @@ -275,6 +275,44 @@ async function runCustomTimerBenchmarks () { cache.clear(); }, iterations); + // Phase 5: Additional API method benchmarks + console.log("Phase 5: Additional API method benchmarks"); + + // keys() + await timer.timeFunction("lru.keys", () => { + phase2Cache.keys(); + }, iterations); + + // values() + await timer.timeFunction("lru.values", () => { + phase2Cache.values(); + }, iterations); + + // entries() + await timer.timeFunction("lru.entries", () => { + phase2Cache.entries(); + }, iterations); + + // setWithEvicted() + const setWithEvictedCache = lru(2); + setWithEvictedCache.set("a", "value_a"); + setWithEvictedCache.set("b", "value_b"); + let setWithEvictedIndex = 0; + await timer.timeFunction("lru.setWithEvicted", () => { + const key = `extra_key_${setWithEvictedIndex}`; + const value = `extra_value_${setWithEvictedIndex}`; + setWithEvictedCache.setWithEvicted(key, value); + setWithEvictedIndex++; + }, iterations); + + // expiresAt() + const expiresCache = lru(cacheSize, 5000); + const expiresKey = "expires_key"; + expiresCache.set(expiresKey, "expires_value"); + await timer.timeFunction("lru.expiresAt", () => { + expiresCache.expiresAt(expiresKey); + }, iterations); + timer.printResults(); } From a2599585fa800242cd2da58b68bc55aff9d35706 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 09:31:58 -0500 Subject: [PATCH 17/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 123 +------------------ 1 file changed, 6 insertions(+), 117 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 5dfa55e..335a765 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -79,14 +79,14 @@ function generateTestData (size) { } async function runPerformanceObserverBenchmarks () { - console.log("šŸ”¬ Performance Observer Benchmarks"); - console.log("==================================="); - console.log("(Using CustomTimer for function-level timing)"); + console.log("šŸ”¬ LRU Performance Benchmarks"); + console.log("=============================="); + console.log("(Using CustomTimer for high-resolution function timing)"); const timer = new CustomTimer(); const cacheSize = 1000; - const testData = generateTestData(cacheSize * 2); const iterations = 10000; + const testData = generateTestData(cacheSize * 2); console.log("Running operations..."); @@ -148,7 +148,7 @@ async function runPerformanceObserverBenchmarks () { const idx = deleteIndices[mixedDeleteIndex % iterations]; phase2Cache.delete(testData[idx].key); mixedDeleteIndex++; - }, 1000); + }, iterations); // Phase 3: Cache eviction stress test console.log("Phase 3: Cache eviction stress test"); @@ -167,114 +167,6 @@ async function runPerformanceObserverBenchmarks () { phase4Cache.clear(); }, iterations); - // Print results with Performance Observer header - console.log("\nšŸ“Š Performance Observer Results"); - console.log("================================"); - - const results = Array.from(timer.results.values()); - console.table(results.map(r => ({ - "Function": r.name, - "Iterations": r.iterations, - "Avg (ms)": r.avgTime.toFixed(4), - "Min (ms)": r.minTime.toFixed(4), - "Max (ms)": r.maxTime.toFixed(4), - "Median (ms)": r.median.toFixed(4), - "Std Dev": r.stdDev.toFixed(4), - "Ops/sec": Math.round(r.opsPerSec) - }))); -} - -async function runCustomTimerBenchmarks () { - console.log("\n⚔ Custom Timer Benchmarks"); - console.log("=========================="); - - const timer = new CustomTimer(); - const cacheSize = 1000; - const iterations = 10000; - const testData = generateTestData(cacheSize * 2); - - console.log("Running operations..."); - - // Phase 1: Fill cache with initial data - console.log("Phase 1: Initial cache population"); - const phase1Cache = lru(cacheSize); - let phase1Index = 0; - await timer.timeFunction("lru.set (initial population)", () => { - const i = phase1Index % cacheSize; - phase1Cache.set(testData[i].key, testData[i].value); - phase1Index++; - }, iterations); - - // Phase 2: Mixed read/write operations - console.log("Phase 2: Mixed operations"); - const phase2Cache = lru(cacheSize); - // Pre-populate for realistic workload - for (let i = 0; i < cacheSize; i++) { - phase2Cache.set(testData[i].key, testData[i].value); - } - - // Deterministic mixed workload that exercises the entire cache without conditionals - const getIndices = new Uint32Array(iterations); - const setIndices = new Uint32Array(iterations); - const hasIndices = new Uint32Array(iterations); - const deleteIndices = new Uint32Array(iterations); - - for (let i = 0; i < iterations; i++) { - const idx = i % cacheSize; - getIndices[i] = idx; - setIndices[i] = idx; - hasIndices[i] = idx; - deleteIndices[i] = idx; - } - - let mixedGetIndex = 0; - await timer.timeFunction("lru.get", () => { - const idx = getIndices[mixedGetIndex % iterations]; - phase2Cache.get(testData[idx].key); - mixedGetIndex++; - }, iterations); - - let mixedSetIndex = 0; - await timer.timeFunction("lru.set", () => { - const idx = setIndices[mixedSetIndex % iterations]; - phase2Cache.set(testData[idx].key, testData[idx].value); - mixedSetIndex++; - }, iterations); - - let mixedHasIndex = 0; - await timer.timeFunction("lru.has", () => { - const idx = hasIndices[mixedHasIndex % iterations]; - phase2Cache.has(testData[idx].key); - mixedHasIndex++; - }, iterations); - - let mixedDeleteIndex = 0; - await timer.timeFunction("lru.delete", () => { - const idx = deleteIndices[mixedDeleteIndex % iterations]; - phase2Cache.delete(testData[idx].key); - mixedDeleteIndex++; - }, iterations); - - // Phase 3: Cache eviction stress test - console.log("Phase 3: Cache eviction stress test"); - const phase3Cache = lru(cacheSize); - let phase3Index = 0; - await timer.timeFunction("lru.set (eviction stress)", () => { - const i = phase3Index; - phase3Cache.set(`evict_key_${i}`, `evict_value_${i}`); - phase3Index++; - }, iterations); - - // Phase 4: Some clear operations - console.log("Phase 4: Clear operations"); - await timer.timeFunction("lru.clear", () => { - const cache = lru(cacheSize); - for (let j = 0; j < 100; j++) { - cache.set(`temp_${j}`, `temp_value_${j}`); - } - cache.clear(); - }, iterations); - // Phase 5: Additional API method benchmarks console.log("Phase 5: Additional API method benchmarks"); @@ -366,13 +258,11 @@ async function runAllPerformanceTests () { try { await runPerformanceObserverBenchmarks(); - await runCustomTimerBenchmarks(); await runScalabilityTest(); console.log("\nāœ… Performance tests completed!"); console.log("\nšŸ“‹ Notes:"); - console.log("- Performance Observer: Uses CustomTimer for function-level timing (PerformanceObserver function entries not supported in this Node.js version)"); - console.log("- Custom Timer: High-resolution timing with statistical analysis"); + console.log("- Benchmarks: High-resolution timing with statistical analysis using CustomTimer (based on performance.now())"); console.log("- Scalability Test: Shows how performance scales with cache size"); } catch (error) { @@ -389,7 +279,6 @@ if (import.meta.url === `file://${process.argv[1]}`) { export { runAllPerformanceTests, runPerformanceObserverBenchmarks, - runCustomTimerBenchmarks, runScalabilityTest, CustomTimer }; From 823d81d583576abc75d4c0e31ab079b3e1c2c10b Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 09:35:02 -0500 Subject: [PATCH 18/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 335a765..7721cea 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -92,9 +92,9 @@ async function runPerformanceObserverBenchmarks () { // Phase 1: Fill cache with initial data console.log("Phase 1: Initial cache population"); - const phase1Cache = lru(cacheSize); let phase1Index = 0; await timer.timeFunction("lru.set (initial population)", () => { + const phase1Cache = lru(cacheSize); const i = phase1Index % cacheSize; phase1Cache.set(testData[i].key, testData[i].value); phase1Index++; From 3eeb1fc147e4b42274fe70fbeb4b51b6fd42755c Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 09:38:37 -0500 Subject: [PATCH 19/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 30 ++++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 7721cea..62f30fa 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -143,6 +143,21 @@ async function runPerformanceObserverBenchmarks () { mixedHasIndex++; }, iterations); + // keys() + await timer.timeFunction("lru.keys", () => { + phase2Cache.keys(); + }, iterations); + + // values() + await timer.timeFunction("lru.values", () => { + phase2Cache.values(); + }, iterations); + + // entries() + await timer.timeFunction("lru.entries", () => { + phase2Cache.entries(); + }, iterations); + let mixedDeleteIndex = 0; await timer.timeFunction("lru.delete", () => { const idx = deleteIndices[mixedDeleteIndex % iterations]; @@ -170,21 +185,6 @@ async function runPerformanceObserverBenchmarks () { // Phase 5: Additional API method benchmarks console.log("Phase 5: Additional API method benchmarks"); - // keys() - await timer.timeFunction("lru.keys", () => { - phase2Cache.keys(); - }, iterations); - - // values() - await timer.timeFunction("lru.values", () => { - phase2Cache.values(); - }, iterations); - - // entries() - await timer.timeFunction("lru.entries", () => { - phase2Cache.entries(); - }, iterations); - // setWithEvicted() const setWithEvictedCache = lru(2); setWithEvictedCache.set("a", "value_a"); From 9f22fe9df833f2dc1f0d10c1b30ed23a291419dc Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 09:46:31 -0500 Subject: [PATCH 20/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 62f30fa..8c75b47 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -92,9 +92,9 @@ async function runPerformanceObserverBenchmarks () { // Phase 1: Fill cache with initial data console.log("Phase 1: Initial cache population"); + const phase1Cache = lru(cacheSize); let phase1Index = 0; await timer.timeFunction("lru.set (initial population)", () => { - const phase1Cache = lru(cacheSize); const i = phase1Index % cacheSize; phase1Cache.set(testData[i].key, testData[i].value); phase1Index++; @@ -198,7 +198,7 @@ async function runPerformanceObserverBenchmarks () { }, iterations); // expiresAt() - const expiresCache = lru(cacheSize, 5000); + const expiresCache = lru(cacheSize, 6e4); const expiresKey = "expires_key"; expiresCache.set(expiresKey, "expires_value"); await timer.timeFunction("lru.expiresAt", () => { From 4dcb87e1f75a8ebb9d811d08da3e1e61c1cb16a5 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 09:59:53 -0500 Subject: [PATCH 21/22] Updating perf benchmarks --- benchmarks/performance-observer-benchmark.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 8c75b47..30a6ea8 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -48,7 +48,7 @@ class CustomTimer { } printResults () { - console.log("\nā±ļø Custom Timer Results"); + console.log("\nā±ļø Performance Results"); console.log("========================"); const results = Array.from(this.results.values()); From e16878e8cfaed219487a79760910d5aae7113d75 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Wed, 14 Jan 2026 10:03:28 -0500 Subject: [PATCH 22/22] Updating perf benchmarks --- README.md | 2 +- benchmarks/performance-observer-benchmark.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ed784b1..a8f5a69 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ npm run benchmark:all # Individual benchmark suites npm run benchmark:modern # Comprehensive Tinybench suite -npm run benchmark:perf # Performance Observer measurements +npm run benchmark:perf # Performance measurements npm run benchmark:comparison # Compare against other LRU libraries ``` diff --git a/benchmarks/performance-observer-benchmark.js b/benchmarks/performance-observer-benchmark.js index 30a6ea8..9929990 100644 --- a/benchmarks/performance-observer-benchmark.js +++ b/benchmarks/performance-observer-benchmark.js @@ -78,7 +78,7 @@ function generateTestData (size) { return out; } -async function runPerformanceObserverBenchmarks () { +async function runPerformanceBenchmarks () { console.log("šŸ”¬ LRU Performance Benchmarks"); console.log("=============================="); console.log("(Using CustomTimer for high-resolution function timing)"); @@ -257,7 +257,7 @@ async function runAllPerformanceTests () { console.log(`Date: ${new Date().toISOString()}`); try { - await runPerformanceObserverBenchmarks(); + await runPerformanceBenchmarks(); await runScalabilityTest(); console.log("\nāœ… Performance tests completed!"); @@ -278,7 +278,7 @@ if (import.meta.url === `file://${process.argv[1]}`) { export { runAllPerformanceTests, - runPerformanceObserverBenchmarks, + runPerformanceBenchmarks, runScalabilityTest, CustomTimer };