| | "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, |
| | }; |
| |
|