| "use strict"; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.OptimizedMemoryStore = exports.ParallelBatchProcessor = exports.VectorOps = exports.TensorBufferManager = exports.Float32BufferPool = exports.LRUCache = exports.PERF_CONSTANTS = void 0; |
| |
| |
| |
| exports.PERF_CONSTANTS = { |
| DEFAULT_CACHE_SIZE: 1000, |
| DEFAULT_BUFFER_POOL_SIZE: 64, |
| DEFAULT_BATCH_SIZE: 32, |
| MIN_PARALLEL_BATCH_SIZE: 8, |
| UNROLL_THRESHOLD: 32, |
| }; |
| |
| |
| |
| |
| class LRUCache { |
| constructor(capacity = exports.PERF_CONSTANTS.DEFAULT_CACHE_SIZE) { |
| this.map = new Map(); |
| this.head = null; |
| this.tail = null; |
| |
| this.hits = 0; |
| this.misses = 0; |
| if (capacity < 1) |
| throw new Error('Cache capacity must be >= 1'); |
| this.capacity = capacity; |
| } |
| |
| |
| |
| get(key) { |
| const node = this.map.get(key); |
| if (!node) { |
| this.misses++; |
| return undefined; |
| } |
| this.hits++; |
| |
| this.moveToHead(node); |
| return node.value; |
| } |
| |
| |
| |
| set(key, value) { |
| const existing = this.map.get(key); |
| if (existing) { |
| |
| existing.value = value; |
| this.moveToHead(existing); |
| return; |
| } |
| |
| const node = { key, value, prev: null, next: null }; |
| |
| if (this.map.size >= this.capacity) { |
| this.evictLRU(); |
| } |
| |
| this.map.set(key, node); |
| this.addToHead(node); |
| } |
| |
| |
| |
| has(key) { |
| return this.map.has(key); |
| } |
| |
| |
| |
| delete(key) { |
| const node = this.map.get(key); |
| if (!node) |
| return false; |
| this.removeNode(node); |
| this.map.delete(key); |
| return true; |
| } |
| |
| |
| |
| clear() { |
| this.map.clear(); |
| this.head = null; |
| this.tail = null; |
| } |
| |
| |
| |
| get size() { |
| return this.map.size; |
| } |
| |
| |
| |
| getStats() { |
| const total = this.hits + this.misses; |
| return { |
| size: this.map.size, |
| capacity: this.capacity, |
| hits: this.hits, |
| misses: this.misses, |
| hitRate: total > 0 ? this.hits / total : 0, |
| }; |
| } |
| |
| |
| |
| resetStats() { |
| this.hits = 0; |
| this.misses = 0; |
| } |
| |
| moveToHead(node) { |
| if (node === this.head) |
| return; |
| this.removeNode(node); |
| this.addToHead(node); |
| } |
| |
| addToHead(node) { |
| node.prev = null; |
| node.next = this.head; |
| if (this.head) { |
| this.head.prev = node; |
| } |
| this.head = node; |
| if (!this.tail) { |
| this.tail = node; |
| } |
| } |
| |
| removeNode(node) { |
| if (node.prev) { |
| node.prev.next = node.next; |
| } |
| else { |
| this.head = node.next; |
| } |
| if (node.next) { |
| node.next.prev = node.prev; |
| } |
| else { |
| this.tail = node.prev; |
| } |
| } |
| |
| evictLRU() { |
| if (!this.tail) |
| return; |
| this.map.delete(this.tail.key); |
| this.removeNode(this.tail); |
| } |
| |
| |
| |
| *entries() { |
| let current = this.head; |
| while (current) { |
| yield [current.key, current.value]; |
| current = current.next; |
| } |
| } |
| } |
| exports.LRUCache = LRUCache; |
| |
| |
| |
| |
| |
| |
| |
| class Float32BufferPool { |
| constructor(maxPoolSize = exports.PERF_CONSTANTS.DEFAULT_BUFFER_POOL_SIZE) { |
| this.pools = new Map(); |
| |
| this.allocations = 0; |
| this.reuses = 0; |
| this.maxPoolSize = maxPoolSize; |
| } |
| |
| |
| |
| acquire(size) { |
| const pool = this.pools.get(size); |
| if (pool && pool.length > 0) { |
| this.reuses++; |
| return pool.pop(); |
| } |
| this.allocations++; |
| return new Float32Array(size); |
| } |
| |
| |
| |
| release(buffer) { |
| const size = buffer.length; |
| let pool = this.pools.get(size); |
| if (!pool) { |
| pool = []; |
| this.pools.set(size, pool); |
| } |
| |
| if (pool.length < this.maxPoolSize) { |
| |
| buffer.fill(0); |
| pool.push(buffer); |
| } |
| } |
| |
| |
| |
| prewarm(sizes, count = 8) { |
| for (const size of sizes) { |
| let pool = this.pools.get(size); |
| if (!pool) { |
| pool = []; |
| this.pools.set(size, pool); |
| } |
| while (pool.length < count) { |
| pool.push(new Float32Array(size)); |
| this.allocations++; |
| } |
| } |
| } |
| |
| |
| |
| clear() { |
| this.pools.clear(); |
| } |
| |
| |
| |
| getStats() { |
| let pooledBuffers = 0; |
| for (const pool of this.pools.values()) { |
| pooledBuffers += pool.length; |
| } |
| const total = this.allocations + this.reuses; |
| return { |
| allocations: this.allocations, |
| reuses: this.reuses, |
| reuseRate: total > 0 ? this.reuses / total : 0, |
| pooledBuffers, |
| }; |
| } |
| } |
| exports.Float32BufferPool = Float32BufferPool; |
| |
| |
| |
| |
| |
| |
| |
| class TensorBufferManager { |
| constructor(pool) { |
| this.workingBuffers = new Map(); |
| this.bufferPool = pool ?? new Float32BufferPool(); |
| } |
| |
| |
| |
| getWorking(name, size) { |
| const existing = this.workingBuffers.get(name); |
| if (existing && existing.length === size) { |
| return existing; |
| } |
| |
| if (existing) { |
| this.bufferPool.release(existing); |
| } |
| const buffer = this.bufferPool.acquire(size); |
| this.workingBuffers.set(name, buffer); |
| return buffer; |
| } |
| |
| |
| |
| getTemp(size) { |
| return this.bufferPool.acquire(size); |
| } |
| |
| |
| |
| releaseTemp(buffer) { |
| this.bufferPool.release(buffer); |
| } |
| |
| |
| |
| releaseAll() { |
| for (const buffer of this.workingBuffers.values()) { |
| this.bufferPool.release(buffer); |
| } |
| this.workingBuffers.clear(); |
| } |
| |
| |
| |
| getPool() { |
| return this.bufferPool; |
| } |
| } |
| exports.TensorBufferManager = TensorBufferManager; |
| |
| |
| |
| |
| |
| |
| |
| exports.VectorOps = { |
| |
| |
| |
| dot(a, b) { |
| const len = a.length; |
| let sum = 0; |
| |
| const unrolled = len - (len % 8); |
| let i = 0; |
| for (; i < unrolled; i += 8) { |
| sum += a[i] * b[i] |
| + a[i + 1] * b[i + 1] |
| + a[i + 2] * b[i + 2] |
| + a[i + 3] * b[i + 3] |
| + a[i + 4] * b[i + 4] |
| + a[i + 5] * b[i + 5] |
| + a[i + 6] * b[i + 6] |
| + a[i + 7] * b[i + 7]; |
| } |
| |
| for (; i < len; i++) { |
| sum += a[i] * b[i]; |
| } |
| return sum; |
| }, |
| |
| |
| |
| normSq(a) { |
| const len = a.length; |
| let sum = 0; |
| const unrolled = len - (len % 8); |
| let i = 0; |
| for (; i < unrolled; i += 8) { |
| sum += a[i] * a[i] |
| + a[i + 1] * a[i + 1] |
| + a[i + 2] * a[i + 2] |
| + a[i + 3] * a[i + 3] |
| + a[i + 4] * a[i + 4] |
| + a[i + 5] * a[i + 5] |
| + a[i + 6] * a[i + 6] |
| + a[i + 7] * a[i + 7]; |
| } |
| for (; i < len; i++) { |
| sum += a[i] * a[i]; |
| } |
| return sum; |
| }, |
| |
| |
| |
| norm(a) { |
| return Math.sqrt(exports.VectorOps.normSq(a)); |
| }, |
| |
| |
| |
| |
| cosine(a, b) { |
| const len = a.length; |
| let dot = 0, normA = 0, normB = 0; |
| |
| const unrolled = len - (len % 4); |
| let i = 0; |
| for (; i < unrolled; i += 4) { |
| const a0 = a[i], a1 = a[i + 1], a2 = a[i + 2], a3 = a[i + 3]; |
| const b0 = b[i], b1 = b[i + 1], b2 = b[i + 2], b3 = b[i + 3]; |
| dot += a0 * b0 + a1 * b1 + a2 * b2 + a3 * b3; |
| normA += a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3; |
| normB += b0 * b0 + b1 * b1 + b2 * b2 + b3 * b3; |
| } |
| for (; i < len; i++) { |
| dot += a[i] * b[i]; |
| normA += a[i] * a[i]; |
| normB += b[i] * b[i]; |
| } |
| const denom = Math.sqrt(normA * normB); |
| return denom > 1e-10 ? dot / denom : 0; |
| }, |
| |
| |
| |
| distanceSq(a, b) { |
| const len = a.length; |
| let sum = 0; |
| const unrolled = len - (len % 8); |
| let i = 0; |
| for (; i < unrolled; i += 8) { |
| const d0 = a[i] - b[i]; |
| const d1 = a[i + 1] - b[i + 1]; |
| const d2 = a[i + 2] - b[i + 2]; |
| const d3 = a[i + 3] - b[i + 3]; |
| const d4 = a[i + 4] - b[i + 4]; |
| const d5 = a[i + 5] - b[i + 5]; |
| const d6 = a[i + 6] - b[i + 6]; |
| const d7 = a[i + 7] - b[i + 7]; |
| sum += d0 * d0 + d1 * d1 + d2 * d2 + d3 * d3 |
| + d4 * d4 + d5 * d5 + d6 * d6 + d7 * d7; |
| } |
| for (; i < len; i++) { |
| const d = a[i] - b[i]; |
| sum += d * d; |
| } |
| return sum; |
| }, |
| |
| |
| |
| distance(a, b) { |
| return Math.sqrt(exports.VectorOps.distanceSq(a, b)); |
| }, |
| |
| |
| |
| add(a, b, out) { |
| const len = a.length; |
| const unrolled = len - (len % 8); |
| let i = 0; |
| for (; i < unrolled; i += 8) { |
| out[i] = a[i] + b[i]; |
| out[i + 1] = a[i + 1] + b[i + 1]; |
| out[i + 2] = a[i + 2] + b[i + 2]; |
| out[i + 3] = a[i + 3] + b[i + 3]; |
| out[i + 4] = a[i + 4] + b[i + 4]; |
| out[i + 5] = a[i + 5] + b[i + 5]; |
| out[i + 6] = a[i + 6] + b[i + 6]; |
| out[i + 7] = a[i + 7] + b[i + 7]; |
| } |
| for (; i < len; i++) { |
| out[i] = a[i] + b[i]; |
| } |
| return out; |
| }, |
| |
| |
| |
| sub(a, b, out) { |
| const len = a.length; |
| const unrolled = len - (len % 8); |
| let i = 0; |
| for (; i < unrolled; i += 8) { |
| out[i] = a[i] - b[i]; |
| out[i + 1] = a[i + 1] - b[i + 1]; |
| out[i + 2] = a[i + 2] - b[i + 2]; |
| out[i + 3] = a[i + 3] - b[i + 3]; |
| out[i + 4] = a[i + 4] - b[i + 4]; |
| out[i + 5] = a[i + 5] - b[i + 5]; |
| out[i + 6] = a[i + 6] - b[i + 6]; |
| out[i + 7] = a[i + 7] - b[i + 7]; |
| } |
| for (; i < len; i++) { |
| out[i] = a[i] - b[i]; |
| } |
| return out; |
| }, |
| |
| |
| |
| scale(a, scalar, out) { |
| const len = a.length; |
| const unrolled = len - (len % 8); |
| let i = 0; |
| for (; i < unrolled; i += 8) { |
| out[i] = a[i] * scalar; |
| out[i + 1] = a[i + 1] * scalar; |
| out[i + 2] = a[i + 2] * scalar; |
| out[i + 3] = a[i + 3] * scalar; |
| out[i + 4] = a[i + 4] * scalar; |
| out[i + 5] = a[i + 5] * scalar; |
| out[i + 6] = a[i + 6] * scalar; |
| out[i + 7] = a[i + 7] * scalar; |
| } |
| for (; i < len; i++) { |
| out[i] = a[i] * scalar; |
| } |
| return out; |
| }, |
| |
| |
| |
| normalize(a) { |
| const norm = exports.VectorOps.norm(a); |
| if (norm > 1e-10) { |
| exports.VectorOps.scale(a, 1 / norm, a); |
| } |
| return a; |
| }, |
| |
| |
| |
| mean(vectors, out) { |
| const n = vectors.length; |
| if (n === 0) |
| return out; |
| const len = out.length; |
| out.fill(0); |
| |
| for (const vec of vectors) { |
| for (let i = 0; i < len; i++) { |
| out[i] += vec[i]; |
| } |
| } |
| |
| const invN = 1 / n; |
| exports.VectorOps.scale(out, invN, out); |
| return out; |
| }, |
| }; |
| |
| |
| |
| |
| class ParallelBatchProcessor { |
| constructor(options = {}) { |
| this.batchSize = options.batchSize ?? exports.PERF_CONSTANTS.DEFAULT_BATCH_SIZE; |
| this.maxConcurrency = options.maxConcurrency ?? 4; |
| } |
| |
| |
| |
| async processBatch(items, processor) { |
| const start = performance.now(); |
| const results = new Array(items.length); |
| |
| if (items.length < exports.PERF_CONSTANTS.MIN_PARALLEL_BATCH_SIZE) { |
| for (let i = 0; i < items.length; i++) { |
| results[i] = await processor(items[i], i); |
| } |
| } |
| else { |
| |
| const chunks = this.chunkArray(items, Math.ceil(items.length / this.maxConcurrency)); |
| let offset = 0; |
| await Promise.all(chunks.map(async (chunk, chunkIndex) => { |
| const chunkOffset = chunkIndex * chunks[0].length; |
| for (let i = 0; i < chunk.length; i++) { |
| results[chunkOffset + i] = await processor(chunk[i], chunkOffset + i); |
| } |
| })); |
| } |
| const totalMs = performance.now() - start; |
| return { |
| results, |
| timing: { |
| totalMs, |
| perItemMs: items.length > 0 ? totalMs / items.length : 0, |
| }, |
| }; |
| } |
| |
| |
| |
| processSync(items, processor) { |
| const start = performance.now(); |
| const results = new Array(items.length); |
| |
| for (let i = 0; i < items.length; i += this.batchSize) { |
| const end = Math.min(i + this.batchSize, items.length); |
| for (let j = i; j < end; j++) { |
| results[j] = processor(items[j], j); |
| } |
| } |
| const totalMs = performance.now() - start; |
| return { |
| results, |
| timing: { |
| totalMs, |
| perItemMs: items.length > 0 ? totalMs / items.length : 0, |
| }, |
| }; |
| } |
| |
| |
| |
| batchSimilarity(queries, corpus, k = 5) { |
| const results = []; |
| for (const query of queries) { |
| const scores = []; |
| for (let i = 0; i < corpus.length; i++) { |
| scores.push({ |
| index: i, |
| score: exports.VectorOps.cosine(query, corpus[i]), |
| }); |
| } |
| |
| scores.sort((a, b) => b.score - a.score); |
| results.push(scores.slice(0, k)); |
| } |
| return results; |
| } |
| chunkArray(arr, chunkSize) { |
| const chunks = []; |
| for (let i = 0; i < arr.length; i += chunkSize) { |
| chunks.push(arr.slice(i, i + chunkSize)); |
| } |
| return chunks; |
| } |
| } |
| exports.ParallelBatchProcessor = ParallelBatchProcessor; |
| |
| |
| |
| class OptimizedMemoryStore { |
| constructor(options = {}) { |
| this.cache = new LRUCache(options.cacheSize ?? exports.PERF_CONSTANTS.DEFAULT_CACHE_SIZE); |
| this.bufferPool = new Float32BufferPool(); |
| this.dimension = options.dimension ?? 384; |
| |
| this.bufferPool.prewarm([this.dimension], 16); |
| } |
| |
| |
| |
| store(id, embedding, content) { |
| |
| const buffer = this.bufferPool.acquire(this.dimension); |
| |
| const emb = embedding instanceof Float32Array ? embedding : new Float32Array(embedding); |
| buffer.set(emb); |
| this.cache.set(id, { |
| id, |
| embedding: buffer, |
| content, |
| score: 1.0, |
| }); |
| } |
| |
| |
| |
| get(id) { |
| return this.cache.get(id); |
| } |
| |
| |
| |
| search(query, k = 5) { |
| const results = []; |
| for (const [, entry] of this.cache.entries()) { |
| const score = exports.VectorOps.cosine(query, entry.embedding); |
| results.push({ entry, score }); |
| } |
| results.sort((a, b) => b.score - a.score); |
| return results.slice(0, k).map(r => ({ ...r.entry, score: r.score })); |
| } |
| |
| |
| |
| delete(id) { |
| const entry = this.cache.get(id); |
| if (entry) { |
| this.bufferPool.release(entry.embedding); |
| } |
| return this.cache.delete(id); |
| } |
| |
| |
| |
| getStats() { |
| return { |
| cache: this.cache.getStats(), |
| buffers: this.bufferPool.getStats(), |
| }; |
| } |
| } |
| exports.OptimizedMemoryStore = OptimizedMemoryStore; |
| |
| |
| |
| exports.default = { |
| LRUCache, |
| Float32BufferPool, |
| TensorBufferManager, |
| VectorOps: exports.VectorOps, |
| ParallelBatchProcessor, |
| OptimizedMemoryStore, |
| PERF_CONSTANTS: exports.PERF_CONSTANTS, |
| }; |
|
|