При работе с ONNX Runtime Web в браузере важной задачей является
оптимизация производительности и обеспечение плавности интерфейса. Часто
модели глубокого обучения используются для обработки кадров в реальном
времени, например, в задачах компьютерного зрения. Прямой вызов
инференса в основном потоке может привести к задержкам рендеринга и
заметным «тормозам» интерфейса. Для решения этой проблемы используется
функция requestAnimationFrame.
requestAnimationFrame предоставляет механизм для
синхронизации работы с частотой обновления дисплея. В отличие от
таймеров setTimeout или setInterval, этот
метод гарантирует вызов функции перед следующей перерисовкой браузера,
что минимизирует визуальные «разрывы» и задержки.
function renderFrame() {
// код инференса и визуализации
requestAnimationFrame(renderFrame);
}
requestAnimationFrame(renderFrame);
ONNX Runtime Web предоставляет интерфейс для выполнения моделей в
браузере с помощью JavaScript. Основной объект —
InferenceSession, который загружает и выполняет модель.
Синхронизация с requestAnimationFrame позволяет запускать
инференс на каждом кадре, не блокируя UI.
Пример инициализации сессии и запуска инференса:
import * as ort from 'onnxruntime-web';
async function initModel() {
const session = await ort.InferenceSession.create('model.onnx');
return session;
}
async function runInference(session, inputTensor) {
const feeds = { input: inputTensor };
const results = await session.run(feeds);
return results.output;
}
Для синхронизации с рендерингом:
let latestFrame = null;
async function processFrame(session) {
if (!latestFrame) return;
const output = await runInference(session, latestFrame);
renderOutput(output);
latestFrame = null;
}
function loop(session) {
requestAnimationFrame(() => loop(session));
processFrame(session);
}
В данном подходе latestFrame хранит последний доступный
кадр. Функция processFrame выполняет инференс асинхронно, а
requestAnimationFrame гарантирует вызов цикла до
перерисовки, обеспечивая плавность анимации.
ONNX Runtime Web поддерживает асинхронные вычисления через
async/await, что критично для совместимости с
requestAnimationFrame. Поскольку рендер не должен
блокироваться, инференс рекомендуется запускать с использованием
Promise, а результаты применять только после завершения
вычислений.
async function loopWithInference(session, frame) {
requestAnimationFrame(() => loopWithInference(session, frame));
if (!frame) return;
runInference(session, frame).then(output => {
renderOutput(output);
});
}
Такой подход позволяет браузеру продолжать перерисовку, не дожидаясь завершения инференса. Это особенно важно для моделей с высокой вычислительной сложностью, где время выполнения одного кадра может превышать 16–17 мс (частота 60 FPS).
В задачах реального времени не всегда требуется обработка каждого
кадра. Для экономии ресурсов можно реализовать пропуск кадров, выполняя
инференс, например, через каждые два или три вызова
requestAnimationFrame.
let frameCounter = 0;
const frameSkip = 2;
function loopWithFrameSkip(session, frame) {
requestAnimationFrame(() => loopWithFrameSkip(session, frame));
frameCounter++;
if (frameCounter % frameSkip !== 0) return;
if (!frame) return;
runInference(session, frame).then(output => renderOutput(output));
}
Этот метод снижает нагрузку на CPU/GPU и предотвращает накопление задержек при инференсе сложных моделей.
Для эффективного использования ONNX Runtime Web данные кадров
необходимо преобразовать в тензоры (ort.Tensor) перед
передачей в модель. Использование Uint8Array,
Float32Array и других массивов позволяет минимизировать
накладные расходы на копирование данных.
function createInputTensor(imageData) {
const { data, width, height } = imageData;
const floatData = new Float32Array(width * height * 3);
for (let i = 0; i < width * height; i++) {
floatData[i * 3 + 0] = data[i * 4 + 0] / 255.0;
floatData[i * 3 + 1] = data[i * 4 + 1] / 255.0;
floatData[i * 3 + 2] = data[i * 4 + 2] / 255.0;
}
return new ort.Tensor('float32', floatData, [1, 3, height, width]);
}
Правильная подготовка входных данных и использование батчей минимизирует задержки и улучшает производительность инференса.
Для визуализации результатов инференса, например, наложения
сегментации или отображения координат объектов, данные можно передавать
в Canvas API или WebGL. Интеграция с requestAnimationFrame
обеспечивает синхронизацию обновления данных модели с перерисовкой
сцены, что исключает мерцания и несогласованность кадров.
function renderOutput(output) {
const ctx = canvas.getContext('2d');
// отрисовка на основе output
}
wasm) или WebGPU
(webgl) бэкенды ONNX Runtime Web для ускорения
инференса.Такой подход обеспечивает плавный пользовательский опыт, поддерживает высокую частоту обновления интерфейса и эффективное использование ресурсов браузера при работе с ONNX Runtime Web.