Асинхронный инференс и конкурентные запросы

ONNX Runtime Web (ORT Web) предоставляет возможности запуска моделей машинного обучения непосредственно в браузере с использованием JavaScript. Особое внимание уделяется обработке асинхронных операций и управлению конкурентными запросами, что критично для производительных веб-приложений, работающих с нейросетевыми моделями.


Асинхронный инференс

Асинхронный инференс позволяет выполнять предсказания модели без блокировки основного потока выполнения JavaScript, что особенно важно в однопоточном окружении браузера. В ORT Web это реализуется через использование промисов и async/await.

Создание сессии асинхронно:

import * as ort from 'onnxruntime-web';

async function createSession(modelUrl) {
    const session = await ort.InferenceSession.create(modelUrl, {
        executionProviders: ['wasm', 'webgl'] // Выбор провайдера вычислений
    });
    return session;
}

Параметр executionProviders определяет способ выполнения модели:

  • wasm — WebAssembly, обеспечивает кроссплатформенность и стабильность, но медленнее GPU.
  • webgl — использование GPU через WebGL, ускоряет вычисления на графическом процессоре.

Асинхронное создание сессии важно для загрузки больших моделей без блокировки интерфейса.

Асинхронный вызов инференса:

async function runInference(session, inputData) {
    const feeds = { input: new ort.Tensor('float32', inputData, [1, 3, 224, 224]) };
    const results = await session.run(feeds);
    return results.output.data;
}

Особенности:

  • session.run(feeds) возвращает промис, который разрешается после завершения инференса.
  • Все операции ввода/вывода данных и вычислений не блокируют главный поток.
  • Важна корректная подготовка тензоров: тип данных (float32, int32) и форма (shape) должны совпадать с определением модели.

Конкурентные запросы

В реальных приложениях может потребоваться запуск нескольких инференсов параллельно. ORT Web позволяет управлять конкурентными запросами, но существует несколько важных ограничений.

Ограничения конкуренции:

  1. Одна сессия не всегда безопасна для одновременного вызова run из разных потоков.
  2. При использовании WebAssembly один поток выполнения обрабатывает инференс последовательно.
  3. WebGL поддерживает многопоточность через использование отдельных контекстов, но при большом числе запросов производительность может падать.

Пример организации очереди запросов:

class InferenceQueue {
    constructor(session) {
        this.session = session;
        this.queue = [];
        this.running = false;
    }

    enqueue(inputData) {
        return new Promise((resolve, reject) => {
            this.queue.push({ inputData, resolve, reject });
            this.processQueue();
        });
    }

    async processQueue() {
        if (this.running || this.queue.length === 0) return;
        this.running = true;

        const { inputData, resolve, reject } = this.queue.shift();
        try {
            const result = await runInference(this.session, inputData);
            resolve(result);
        } catch (error) {
            reject(error);
        } finally {
            this.running = false;
            this.processQueue();
        }
    }
}

Преимущества такого подхода:

  • Исключается одновременный вызов session.run одной сессии.
  • Легко масштабируется для нескольких сессий: можно создавать пул из нескольких экземпляров модели.
  • Поддерживается приоритетная обработка, если очередь модифицировать для сортировки задач.

Параллельное выполнение с пулом сессий

Для действительно высокой нагрузки рекомендуется создавать пул сессий:

async function createSessionPool(modelUrl, poolSize) {
    const sessions = [];
    for (let i = 0; i < poolSize; i++) {
        const session = await createSession(modelUrl);
        sessions.push(session);
    }
    return sessions;
}

function getNextSession(sessions) {
    const session = sessions.shift();
    sessions.push(session);
    return session;
}

Использование пула позволяет:

  • Распределять запросы между несколькими сессиями, снижая задержки.
  • Максимально использовать возможности WebGL и WebAssembly.
  • Параллельно обрабатывать несколько инференсов без блокировки интерфейса.

Оптимизация асинхронного инференса

Ключевые подходы к оптимизации:

  • Пакетирование запросов: объединение нескольких входных данных в один батч увеличивает пропускную способность.
  • Кэширование сессий и результатов: повторное использование сессий и хранение промежуточных результатов уменьшает время загрузки модели.
  • Выбор провайдера вычислений: WebGL для GPU, WASM для стабильности и кроссплатформенности.
  • Контроль памяти: тензоры больших размеров занимают значительные ресурсы, поэтому важно освобождать их после использования.

Практические советы

  1. Всегда использовать async/await для вызова session.run, чтобы избежать блокировки UI.
  2. Для конкурентных запросов применять очередь или пул сессий, особенно при высоких нагрузках.
  3. Проверять поддержку WebGL и WebAssembly в целевом браузере перед выбором провайдера.
  4. Тестировать производительность на разных устройствах, так как ресурсы CPU и GPU сильно различаются.

Асинхронный инференс и грамотное управление конкурентными запросами являются критически важными аспектами при работе с ONNX Runtime Web, позволяя строить высокопроизводительные и отзывчивые веб-приложения для обработки нейросетевых моделей.