Files
wifi-densepose/npm/packages/ruvector/test/benchmark-perf.js
ruv d803bfe2b1 Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
2026-02-28 14:39:40 -05:00

251 lines
11 KiB
JavaScript

const { LRUCache, Float32BufferPool, VectorOps, ParallelBatchProcessor, OptimizedMemoryStore } = require('../dist/core/neural-perf.js');
// Benchmark utilities
function benchmark(name, fn, iterations = 10000) {
// Warmup
for (let i = 0; i < 100; i++) fn();
const start = performance.now();
for (let i = 0; i < iterations; i++) fn();
const elapsed = performance.now() - start;
return { name, iterations, totalMs: elapsed, perOpUs: (elapsed / iterations) * 1000 };
}
function formatResult(result) {
const name = result.name.padEnd(40);
const us = result.perOpUs.toFixed(3).padStart(10);
return name + ' ' + us + ' us/op (' + result.iterations + ' ops in ' + result.totalMs.toFixed(1) + 'ms)';
}
console.log('\n═══════════════════════════════════════════════════════════════════');
console.log(' RUVECTOR PERFORMANCE BENCHMARKS');
console.log('═══════════════════════════════════════════════════════════════════\n');
// ============================================================================
// 1. LRU Cache: O(1) vs Map (simulated O(n) eviction)
// ============================================================================
console.log('┌─────────────────────────────────────────────────────────────────┐');
console.log('│ LRU CACHE BENCHMARK │');
console.log('└─────────────────────────────────────────────────────────────────┘');
const lruCache = new LRUCache(1000);
const naiveCache = new Map();
const CACHE_SIZE = 1000;
// Pre-fill caches
for (let i = 0; i < CACHE_SIZE; i++) {
lruCache.set('key' + i, { data: i });
naiveCache.set('key' + i, { data: i });
}
// Benchmark LRU get (O(1))
const lruGet = benchmark('LRU Cache get (O(1))', () => {
lruCache.get('key' + Math.floor(Math.random() * CACHE_SIZE));
}, 100000);
// Benchmark LRU set with eviction (O(1))
let lruSetCounter = CACHE_SIZE;
const lruSet = benchmark('LRU Cache set+evict (O(1))', () => {
lruCache.set('newkey' + lruSetCounter++, { data: lruSetCounter });
}, 50000);
// Simulate naive O(n) eviction
const naiveEvict = benchmark('Naive Map eviction (O(n))', () => {
// Simulate finding oldest entry (O(n) scan)
let oldest = null;
for (const [k, v] of naiveCache.entries()) {
oldest = k;
break; // Just get first (simulating finding oldest)
}
naiveCache.delete(oldest);
naiveCache.set('key' + Math.random(), { data: 1 });
}, 50000);
console.log(formatResult(lruGet));
console.log(formatResult(lruSet));
console.log(formatResult(naiveEvict));
console.log(' → LRU Speedup: ' + (naiveEvict.perOpUs / lruSet.perOpUs).toFixed(1) + 'x faster\n');
// ============================================================================
// 2. Buffer Pool vs Fresh Allocation
// ============================================================================
console.log('┌─────────────────────────────────────────────────────────────────┐');
console.log('│ BUFFER POOL BENCHMARK │');
console.log('└─────────────────────────────────────────────────────────────────┘');
const bufferPool = new Float32BufferPool(64);
bufferPool.prewarm([384], 32);
// Pooled allocation
const pooledAlloc = benchmark('Buffer Pool acquire+release', () => {
const buf = bufferPool.acquire(384);
buf[0] = 1.0; // Use it
bufferPool.release(buf);
}, 100000);
// Fresh allocation
const freshAlloc = benchmark('Fresh Float32Array allocation', () => {
const buf = new Float32Array(384);
buf[0] = 1.0; // Use it
// Let GC handle it
}, 100000);
console.log(formatResult(pooledAlloc));
console.log(formatResult(freshAlloc));
console.log(' → Pool Speedup: ' + (freshAlloc.perOpUs / pooledAlloc.perOpUs).toFixed(1) + 'x faster');
const poolStats = bufferPool.getStats();
console.log(' → Pool Stats: reuse=' + (poolStats.reuseRate * 100).toFixed(1) + '%, pooled=' + poolStats.pooledBuffers + '\n');
// ============================================================================
// 3. Vector Ops: Unrolled vs Standard
// ============================================================================
console.log('┌─────────────────────────────────────────────────────────────────┐');
console.log('│ VECTOR OPERATIONS BENCHMARK (384-dim) │');
console.log('└─────────────────────────────────────────────────────────────────┘');
const vecA = new Float32Array(384);
const vecB = new Float32Array(384);
for (let i = 0; i < 384; i++) {
vecA[i] = Math.random();
vecB[i] = Math.random();
}
// Unrolled cosine
const unrolledCosine = benchmark('VectorOps.cosine (8x unrolled)', () => {
VectorOps.cosine(vecA, vecB);
}, 100000);
// Standard cosine
function standardCosine(a, b) {
let dot = 0, normA = 0, normB = 0;
for (let i = 0; i < a.length; i++) {
dot += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dot / Math.sqrt(normA * normB);
}
const stdCosine = benchmark('Standard cosine (no unroll)', () => {
standardCosine(vecA, vecB);
}, 100000);
console.log(formatResult(unrolledCosine));
console.log(formatResult(stdCosine));
console.log(' → Unroll Speedup: ' + (stdCosine.perOpUs / unrolledCosine.perOpUs).toFixed(2) + 'x faster\n');
// Unrolled dot product
const unrolledDot = benchmark('VectorOps.dot (8x unrolled)', () => {
VectorOps.dot(vecA, vecB);
}, 100000);
function standardDot(a, b) {
let sum = 0;
for (let i = 0; i < a.length; i++) sum += a[i] * b[i];
return sum;
}
const stdDot = benchmark('Standard dot product', () => {
standardDot(vecA, vecB);
}, 100000);
console.log(formatResult(unrolledDot));
console.log(formatResult(stdDot));
console.log(' → Unroll Speedup: ' + (stdDot.perOpUs / unrolledDot.perOpUs).toFixed(2) + 'x faster\n');
// Unrolled distance
const unrolledDist = benchmark('VectorOps.distance (8x unrolled)', () => {
VectorOps.distance(vecA, vecB);
}, 100000);
function standardDistance(a, b) {
let sum = 0;
for (let i = 0; i < a.length; i++) {
const d = a[i] - b[i];
sum += d * d;
}
return Math.sqrt(sum);
}
const stdDist = benchmark('Standard distance', () => {
standardDistance(vecA, vecB);
}, 100000);
console.log(formatResult(unrolledDist));
console.log(formatResult(stdDist));
console.log(' → Unroll Speedup: ' + (stdDist.perOpUs / unrolledDist.perOpUs).toFixed(2) + 'x faster\n');
// ============================================================================
// 4. Batch Processing
// ============================================================================
console.log('┌─────────────────────────────────────────────────────────────────┐');
console.log('│ BATCH SIMILARITY SEARCH │');
console.log('└─────────────────────────────────────────────────────────────────┘');
const corpus = [];
for (let i = 0; i < 1000; i++) {
const v = new Float32Array(384);
for (let j = 0; j < 384; j++) v[j] = Math.random();
corpus.push(v);
}
const queries = [];
for (let i = 0; i < 100; i++) {
const v = new Float32Array(384);
for (let j = 0; j < 384; j++) v[j] = Math.random();
queries.push(v);
}
const batchProcessor = new ParallelBatchProcessor({ batchSize: 32 });
const batchSearch = benchmark('Batch similarity (10 queries x 100 corpus)', () => {
batchProcessor.batchSimilarity(queries.slice(0, 10), corpus.slice(0, 100), 5);
}, 100);
console.log(formatResult(batchSearch));
// ============================================================================
// 5. OptimizedMemoryStore
// ============================================================================
console.log('\n┌─────────────────────────────────────────────────────────────────┐');
console.log('│ OPTIMIZED MEMORY STORE │');
console.log('└─────────────────────────────────────────────────────────────────┘');
const store = new OptimizedMemoryStore({ cacheSize: 1000, dimension: 384 });
// Pre-fill
for (let i = 0; i < 1000; i++) {
const emb = new Float32Array(384);
for (let j = 0; j < 384; j++) emb[j] = Math.random();
store.store('mem' + i, emb, 'content ' + i);
}
const storeGet = benchmark('OptimizedMemoryStore.get (O(1))', () => {
store.get('mem' + Math.floor(Math.random() * 1000));
}, 100000);
const queryEmb = new Float32Array(384);
for (let j = 0; j < 384; j++) queryEmb[j] = Math.random();
const storeSearch = benchmark('OptimizedMemoryStore.search (k=5)', () => {
store.search(queryEmb, 5);
}, 1000);
console.log(formatResult(storeGet));
console.log(formatResult(storeSearch));
const storeStats = store.getStats();
console.log(' → Cache: hits=' + storeStats.cache.hits + ', hitRate=' + (storeStats.cache.hitRate * 100).toFixed(1) + '%\n');
// ============================================================================
// Summary
// ============================================================================
console.log('═══════════════════════════════════════════════════════════════════');
console.log(' SUMMARY');
console.log('═══════════════════════════════════════════════════════════════════');
console.log(' ✓ LRU Cache: O(1) operations, ' + (naiveEvict.perOpUs / lruSet.perOpUs).toFixed(0) + 'x faster than naive eviction');
console.log(' ✓ Buffer Pool: ' + (freshAlloc.perOpUs / pooledAlloc.perOpUs).toFixed(1) + 'x faster, ' + (poolStats.reuseRate * 100).toFixed(0) + '% reuse rate');
console.log(' ✓ Vector Ops: ' + (stdCosine.perOpUs / unrolledCosine.perOpUs).toFixed(1) + 'x faster with 8x unrolling');
console.log(' ✓ Memory Store: O(1) lookup at ' + storeGet.perOpUs.toFixed(3) + ' µs/op');
console.log('═══════════════════════════════════════════════════════════════════\n');