ONNX Runtime Web (ORT Web) предоставляет высокопроизводительное выполнение моделей машинного обучения прямо в браузере или Node.js. Одной из ключевых возможностей для повышения пропускной способности является организация нескольких воркеров и очередей задач для параллельного выполнения инференса. Это особенно актуально для веб-приложений с высокой нагрузкой, где требуется обработка большого числа запросов без блокировки основного потока.
Воркеры — это отдельные потоки исполнения,
изолированные от основного JavaScript-потока. В ORT Web они используются
для запуска моделей в WebAssembly (WASM) или
WebGPU. Каждому воркеру выделяется собственная сессия ONNX
Runtime, что позволяет выполнять инференс независимо от других
воркеров.
Ключевые особенности:
Пример создания воркеров в браузере:
const worker = new Worker('inferenceWorker.js');
worker.postMessage({ type: 'init', modelUrl: 'model.onnx' });
Внутри inferenceWorker.js реализуется загрузка модели и
обработка сообщений:
import * as ort from 'onnxruntime-web';
let session;
self.onmess age = async (event) => {
const { type, inputData, requestId } = event.data;
if (type === 'init') {
session = await ort.InferenceSession.create(event.data.modelUrl);
self.postMessage({ type: 'ready' });
}
if (type === 'infer') {
const feeds = { input: new ort.Tensor('float32', inputData, [1, 3, 224, 224]) };
const results = await session.run(feeds);
self.postMessage({ type: 'result', result: results.output.data, requestId });
}
};
Организация очереди задач позволяет равномерно распределять нагрузку между воркерами. Каждая задача помещается в очередь и назначается свободному воркеру по принципу round-robin или по свободной емкости.
Основные компоненты:
Пример простого диспетчера:
class InferenceQueue {
constructor(workers) {
this.workers = workers;
this.taskQueue = [];
this.activeTasks = new Map();
this.workerIndex = 0;
}
enqueue(task) {
return new Promise((resolve) => {
this.taskQueue.push({ task, resolve });
this.dispatch();
});
}
dispatch() {
if (this.taskQueue.length === 0) return;
const worker = this.workers[this.workerIndex];
this.workerIndex = (this.workerIndex + 1) % this.workers.length;
const { task, resolve } = this.taskQueue.shift();
const requestId = Math.random().toString(36).substr(2, 9);
this.activeTasks.set(requestId, resolve);
worker.postMessage({ type: 'infer', inputData: task.input, requestId });
worker.onmess age = (event) => {
if (event.data.type === 'result') {
const res = this.activeTasks.get(event.data.requestId);
res(event.data.result);
this.activeTasks.delete(event.data.requestId);
this.dispatch();
}
};
}
}
Преимущества такого подхода:
Каждый воркер использует собственный контекст ONNX Runtime, что увеличивает потребление памяти. Для уменьшения нагрузки можно:
При поддержке WebGPU воркеры могут выполнять инференс на GPU, значительно ускоряя обработку. Особенности:
GPUDevice.Пример инициализации с WebGPU:
const session = await ort.InferenceSession.create(modelUrl, {
executionProviders: ['webgpu']
});
При параллельном инференсе важно обрабатывать возможные ошибки:
Пример тайм-аута задачи:
const result = await Promise.race([
queue.enqueue({ input: inputData }),
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 5000))
]);
SharedArrayBuffer или оптимизированные
структуры тензоров.Эта архитектура позволяет эффективно использовать ONNX Runtime Web для параллельного инференса, обеспечивая низкую задержку и высокую пропускную способность при работе с несколькими запросами одновременно.