Минимизация копирования данных: zero-copy и разделяемая память

ONNX Runtime Web предоставляет возможность выполнять модели машинного обучения прямо в браузере или в Node.js с максимальной эффективностью. Одним из критически важных аспектов высокой производительности является минимизация избыточного копирования данных между различными средами выполнения — памятью JavaScript и нативными бэкендами. В этой задаче ключевую роль играют концепции zero-copy и разделяемой памяти.


Zero-copy: принцип и реализация

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

Ключевые моменты реализации zero-copy в Jav * aScript:

  1. TypedArray как основной контейнер данных Для передачи тензоров используются структуры типа Float32Array, Int32Array или Uint8Array. Эти массивы располагаются в contiguous memory и могут быть напрямую отображены в память нативного бэкенда.

    const inputTensorData = new Float32Array([1.0, 2.0, 3.0, 4.0]);
  2. Использование OrtTensor с внешней памятью ONNX Runtime Web позволяет создавать тензоры, указывая на уже существующий буфер, вместо того чтобы копировать данные:

    const tensor = new ort.Tensor('float32', inputTensorData, [2, 2]);

    Здесь inputTensorData не копируется, а используется напрямую в вычислениях модели. Это особенно важно для больших массивов данных, где копирование может занимать миллисекунды и больше.

  3. Совместимость с WebAssembly и WebGPU Бэкенды ONNX Runtime Web (WebAssembly, WebGPU) способны напрямую работать с буферами, выделенными в JavaScript. Это позволяет обойти лишние операции копирования между heap JS и памятью бэкенда.


Разделяемая память

Разделяемая память (shared memory) позволяет нескольким компонентам использовать один и тот же участок памяти. В контексте ONNX Runtime Web это обеспечивает:

  • Быстрый обмен данными между ядрами WebAssembly
  • Эффективное взаимодействие с WebGPU
  • Минимизацию выделений памяти и сборки мусора

Практическая реализация:

  1. SharedArrayBuffer В браузере можно использовать SharedArrayBuffer, который предоставляет доступ к общему буферу из разных потоков Web Workers:

    const sharedBuffer = new SharedArrayBuffer(Float32Array.BYTES_PER_ELEMENT * 1024);
    const sharedArray = new Float32Array(sharedBuffer);

    Этот буфер можно использовать для создания тензора ONNX Runtime Web:

    const tensor = new ort.Tensor('float32', sharedArray, [256, 4]);
  2. Передача тензоров между Web Workers Использование shared memory позволяет нескольким воркерам обрабатывать данные параллельно, не создавая лишних копий. В больших приложениях это уменьшает задержки и снижает нагрузку на сборщик мусора.

  3. Совместимость с zero-copy Zero-copy и shared memory работают вместе: создавая тензор на основе SharedArrayBuffer, данные не копируются ни при создании тензора, ни при передаче в вычислительный бэкенд.


Особенности управления памятью

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

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

  • Синхронизация при совместной работе При использовании SharedArrayBuffer необходимо учитывать синхронизацию между потоками, чтобы избежать гонок данных. В ONNX Runtime Web большинство операций чтения/записи тензоров атомарны, но комплексные вычисления требуют явной координации.


Примеры эффективного использования

  1. Обработка потокового видео Использование zero-copy позволяет подавать кадры из ImageData напрямую в модель, без промежуточного копирования в массивы Float32Array.

  2. Пакетная обработка данных Разделяемая память позволяет создавать большой буфер для пакета входных тензоров и многократно использовать его для разных батчей, что снижает нагрузку на память и ускоряет инференс.

  3. WebGPU интеграция Тензоры, созданные на основе SharedArrayBuffer, могут быть привязаны к буферам GPU, что минимизирует копирование между CPU и GPU и ускоряет вычисления до нескольких раз по сравнению с традиционным подходом.


Zero-copy и разделяемая память являются фундаментальными инструментами оптимизации ONNX Runtime Web, позволяя достигать максимальной скорости и эффективности при работе с большими моделями и потоковыми данными в браузере или Node.js. Правильное использование этих механизмов позволяет минимизировать накладные расходы на память и обеспечивает стабильную высокопроизводительную работу приложений машинного обучения.