Rollup и esbuild: особенности бандлинга wasm

ONNX Runtime Web (ORT Web) предоставляет возможность запускать модели ONNX в браузере, используя WebAssembly (WASM) или WebGL для вычислений. Для начала работы библиотеку можно подключить через npm:

npm install onnxruntime-web

В ES-модулях импорт осуществляется следующим образом:

import * as ort from 'onnxruntime-web';

Для сценариев с классическим <script> можно использовать CDN:

<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js"></script>

Библиотека поставляется в нескольких вариантах, включая версии с поддержкой WebAssembly SIMD и WebGL. Выбор зависит от требований к производительности и поддержке браузеров.


Инициализация и загрузка модели

Основной объект для работы — InferenceSession. Его создание включает указание пути к ONNX модели и выбор бэкенда:

const session = await ort.InferenceSession.create('model.onnx', {
  executionProviders: ['wasm'] // или 'webgl'
});

Ключевые моменты:

  • executionProviders позволяет выбирать движок вычислений. Для браузеров чаще всего используются wasm или webgl.
  • Для больших моделей рекомендуется предварительно загружать WASM бинарь асинхронно, чтобы избежать блокировки интерфейса.

Пример асинхронной подготовки с загрузкой WASM:

await ort.env.wasm.wasmPaths.set('https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/wasm/');

Подготовка данных для инференса

Модели ONNX ожидают данные в формате ort.Tensor. Для числовых входов используется тип float32:

const inputTensor = new ort.Tensor('float32', new Float32Array([1.0, 2.0, 3.0]), [3]);

Важные моменты:

  • Порядок элементов в массиве должен соответствовать ожидаемому моделью формату (row-major).
  • Размерность массива (dims) обязательно указывается, иначе инференс завершится ошибкой.
  • Поддерживаются типы: float32, int32, bool, string.

Выполнение инференса

Инференс выполняется методом run, который принимает объект с входными тензорами:

const feeds = { input: inputTensor };
const results = await session.run(feeds);
const outputTensor = results.output;

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

  • Метод run возвращает объект, где ключи соответствуют именам выходов модели.
  • Рекомендуется использовать асинхронную форму await, так как WebAssembly выполняется в отдельном потоке.

Оптимизация загрузки с Rollup и esbuild

При бандлинге ONNX Runtime Web возникают специфические нюансы:

Работа с WebAssembly

  • ort.wasm — отдельный бинарный файл, который нужно правильно подключать. Rollup и esbuild не включают его в основной JS бандл автоматически.
  • Для esbuild используется file или asset плагин, чтобы WASM копировался в dist и путь к нему можно было указать через wasmPaths.set.

Пример конфигурации esbuild:

import wasm from 'esbuild-plugin-wasm';

esbuild.build({
  entryPoints: ['src/index.js'],
  bundle: true,
  outdir: 'dist',
  plugins: [wasm()],
});

Для Rollup используется плагин rollup-plugin-copy или rollup-plugin-url, чтобы WASM-файл оказался в конечной сборке.

Динамический импорт

Для оптимизации размера бандла рекомендуется загружать WASM динамически:

const ortModule = await import('onnxruntime-web');
await ortModule.env.wasm.wasmPaths.set('/dist/ort-wasm.wasm');

Это позволяет:

  • Сократить начальный размер JS.
  • Загружать тяжелые бинарные файлы по мере необходимости.

Настройка tree-shaking

  • Библиотека экспортирует много функционала, но для браузерных бандлов нужны только InferenceSession и Tensor.
  • ES-модули и modern bundlers автоматически удаляют неиспользуемый код, если импортировать из onnxruntime-web частично:
import { InferenceSession, Tensor } from 'onnxruntime-web';

Особенности WebGL провайдера

  • WebGL провайдер позволяет ускорить инференс с использованием GPU.
  • При Rollup или esbuild важно, чтобы все шейдеры и бинарные ресурсы корректно копировались, иначе инициализация WebGL завершится ошибкой.
  • Производительность зависит от размера модели, так как WebGL ограничен памятью видеокарты.

Отладка и мониторинг

  • Для проверки использования провайдера: session.sessionOptions.executionProviders.
  • Для анализа производительности инференса можно использовать встроенные таймеры JavaScript (performance.now()).
const t0 = performance.now();
await session.run(feeds);
const t1 = performance.now();
console.log(`Inference time: ${t1 - t0} ms`);
  • Ошибки при несовпадении формата входных данных или размеров тензоров чаще всего проявляются в момент вызова run.

Практические рекомендации

  • Большие модели следует предварительно оптимизировать средствами ONNX (например, onnx-simplifier) перед использованием в браузере.
  • WASM SIMD версии дают прирост производительности на современных процессорах.
  • Для приложений с множеством моделей лучше использовать динамическую подгрузку import() вместо включения всего в один бандл.
  • В Rollup и esbuild необходимо явно настраивать обработку бинарных файлов, иначе загрузка WASM завершится ошибкой.