llm-semantic-visualizer / src /core /WorkerPool.js
hbauzan's picture
Release v4.17.7 — sync from GitHub main
819a47f
Raw
History Blame Contribute Delete
4.64 kB
/**
* WorkerPool.js
* Hardware-scaling worker thread pool manager with zero-copy transfers and synchronous fallbacks.
*/
import {
executeResampleZFallback,
executeDeltaAndSimilarityFallback,
executeVarianceSortFallback
} from '../visualizer/ReliefMath.js';
export class WorkerPoolManager {
constructor(size = null) {
this.poolSize = size || Math.max(1, Math.min((typeof navigator !== 'undefined' && navigator.hardwareConcurrency) || 4, 8));
this.workers = [];
this.idleWorkers = [];
this.taskQueue = [];
this.activeTransactions = new Map();
this.transactionId = 0;
this.isSupported = typeof window !== 'undefined' && typeof window.Worker !== 'undefined';
if (this.isSupported) {
this.initializePool();
}
}
initializePool() {
for (let i = 0; i < this.poolSize; i++) {
try {
const worker = new Worker(
new URL('../workers/mathWorker.js', import.meta.url),
{ type: 'module' }
);
worker.onmessage = (e) => this.handleMessage(i, e.data);
worker.onerror = (err) => this.handleError(i, err);
this.workers.push(worker);
this.idleWorkers.push(i);
} catch (e) {
this.isSupported = false;
break;
}
}
}
postTask(taskName, payload, transferables = []) {
if (!this.isSupported) {
return this.runFallback(taskName, payload);
}
return new Promise((resolve, reject) => {
const currentTxId = this.transactionId++;
const task = {
transactionId: currentTxId,
taskName,
payload,
transferables,
resolve,
reject
};
this.activeTransactions.set(currentTxId, task);
this.taskQueue.push(task);
this.processQueue();
});
}
processQueue() {
if (this.taskQueue.length === 0 || this.idleWorkers.length === 0) return;
const workerIdx = this.idleWorkers.pop();
const task = this.taskQueue.shift();
const worker = this.workers[workerIdx];
try {
worker.postMessage({
task: task.taskName,
payload: task.payload,
transactionId: task.transactionId
}, task.transferables);
} catch (err) {
this.handleError(workerIdx, err);
}
}
handleMessage(workerIdx, messageData) {
const { transactionId, status, data, similarity, sortedIndices, variances, error } = messageData;
const task = this.activeTransactions.get(transactionId);
if (!task) return;
this.activeTransactions.delete(transactionId);
this.idleWorkers.push(workerIdx);
if (status === 'success') {
task.resolve({ data, similarity, sortedIndices, variances });
} else {
task.reject(new Error(error));
}
this.processQueue();
}
handleError(workerIdx, error) {
console.warn('[WorkerPool] Error en worker. Cambiando a ejecución síncrona.', error);
this.isSupported = false;
if (this.workers[workerIdx]) {
try { this.workers[workerIdx].terminate(); } catch (_) {}
}
for (const [txId, task] of this.activeTransactions.entries()) {
this.runFallback(task.taskName, task.payload)
.then(task.resolve)
.catch(task.reject);
this.activeTransactions.delete(txId);
}
}
runFallback(taskName, payload) {
return new Promise((resolve, reject) => {
try {
switch (taskName) {
case 'resampleMatrixZ': {
const { sourceData, sourceZ, sourceX, targetZ, targetX } = payload;
const res = executeResampleZFallback(sourceData, sourceZ, sourceX, targetZ, targetX);
resolve({ data: res });
break;
}
case 'computeDeltaAndSimilarity': {
const { matrixA, matrixB, length } = payload;
const res = executeDeltaAndSimilarityFallback(matrixA, matrixB, length);
resolve({ data: res.delta, similarity: res.similarity });
break;
}
case 'sortDimensionsByVariance': {
const { data, rows, cols } = payload;
const res = executeVarianceSortFallback(data, rows, cols);
resolve({ sortedIndices: res.indices, variances: res.variances });
break;
}
default:
reject(new Error(`Fallback unsupported for task: ${taskName}`));
}
} catch (err) {
reject(err);
}
});
}
terminateAll() {
this.workers.forEach(w => {
try { w.terminate(); } catch (_) {}
});
this.workers = [];
this.idleWorkers = [];
this.activeTransactions.clear();
}
}
export const WorkerPool = new WorkerPoolManager();