Rockchip · AI & Linux Hub

AI-инференс

RKNN2 SDK V2.5.0 · Нулевое копирование и ограничения входов/выходов

RKNN2 SDK V2.5.0 · Нулевое копирование и ограничения входов/выходов · Полная серия руководства разработчика

· ArmSoM Editorial · Rockchip source documentation · 9 мин чтения

Справочник платформы по руководству Rockchip RKNN2 SDK V2.5.0. Рассматриваются только перечисленные в исходном документе SoC, Linux/Android Runtime и Toolkit. Возможности действуют при указанных в каждом разделе условиях для чипа, SDK, драйвера и ядра и не означают поддержку всех плат ArmSoM. Перед развёртыванием проверьте BSP, драйвер NPU, Runtime и версию модели конкретной платы. Проверка на оборудовании ArmSoM не проводилась; журналы, показатели и примеры взяты из оригинала.

Источники и вычитка: китайская версия следует китайскому V2.5.0 (182 страницы), английская, русская и немецкая — английскому V2.5.0 (208 страниц). Различия оригиналов сохранены; сведения старой версии не выдаются за новые. Китайские рисунки взяты из китайского PDF, остальные — из английского, включая отдельно извлечённые векторные схемы. Номера разделов и формул сохранены. Некоторые ссылки используют старую нумерацию; примеры динамической формы содержат «1224+140=264», утверждения о TP_NORM противоречат друг другу, а init()/rknn.val_perf() отличаются от последующих описаний API. Эти сомнения сохранены: перед выполнением проверьте реальные заголовки и справочник V2.5.0. Код и параметры сохранены, разрывы PDF и продолжения таблиц восстановлены.

Rockchip RKNN2 SDK V2.5.0

RKNN2 SDK V2.5.0 · 8/22

1. Программный стек и основные понятия

2. Среда разработки, версии и подключение платы

3. Процесс разработки и выбор интерфейса

4. Преобразование моделей и редактирование ONNX

5. Квантование, гибридное квантование и QAT

6. Инференс, оценка точности, производительности и памяти

7. Жизненный цикл Runtime, размещение данных и память

Нулевое копирование и ограничения входов/выходов

9. Развёртывание через C API и генерация кода

10. Развёртывание Python через Toolkit Lite2

11. Matmul API и размещение матриц

12. Многоядерная настройка NPU и производительность

13. Модели с динамической формой и развёртывание

14. Многопакетный инференс

15. SRAM NPU RK3588/RK3576

16. Согласованность кэша и разреженный инференс

17. Пользовательские операторы: Python и C API

18. Развёртывание зашифрованных моделей

19. Диагностика точности

20. Анализ производительности и оптимизация сети

21. Анализ, совместное использование и повторное использование памяти

22. Вопросы, обработка ошибок и справочные ресурсы

3.1.6 Интерфейс нулевого копирования RKNN Runtime

3.1.6.1 Введение в нулевое копирование

RK3562 / RK3566 / RK3568 / RK3576 / RK3588 / RV1126B предоставляют общий API и API нулевого копирования; серии RV1103 / RV1106 поддерживают API нулевого копирования. Примеры находятся в examples/rknn_zero_copy проекта rknpu2. Репозиторий и дополнительные сведения: https://github.com/airockchip/rknn-toolkit2/tree/master/rknpu2.

При инференсе RKNN исходные данные проходят обработку входа, выполнение модели NPU и обработку выхода. В зависимости от входного формата и квантования интерфейсы реализуют два процесса: общий API и нулевое копирование, показанные на рисунках 3-5 и 3-6. Главное различие состоит в том, что при каждом обновлении кадра общий интерфейс копирует данные внешнего модуля во входную память NPU Runtime, а интерфейс нулевого копирования непосредственно использует заранее выделенную память — самим Runtime либо внешним фреймворком, например DRM. Это сокращает копирование, повышает производительность и уменьшает расход пропускной способности. Если входные данные имеют только виртуальный адрес, доступен лишь общий API; при физическом адресе или fd доступны оба. Смешивать вызовы двух групп API нельзя.

Технический рисунок оригинала

Рисунок 3-5. Обработка данных общего API

Технический рисунок оригинала

Рисунок 3-6. Обработка данных API нулевого копирования

1 Обычный API

В общем API, показанном на рисунке 3-5, нормализация, квантование, преобразование размещения и деквантование выполняются на CPU, а модель — на NPU. Если соблюдены требования входа нулевого копирования, нормализация и квантование выполняются на NPU, но CPU всё равно копирует входные данные в буфер модели.

1 API нулевого копирования

Нулевое копирование на рисунке 3-6 оптимизирует общий процесс: нормализация, квантование и инференс выполняются на NPU, а преобразование размещения выхода и деквантование — на CPU или NPU. Обработка входного потока эффективнее общего API.

Условия использования нулевого копирования приведены в таблице:

Таблица 3-2. Требования к входу нулевого копирования

Размерность входа RV1103B / RV1106B / RK3566 / RK3568 RK3562 / RK3576 / RK3588 / RV1103 / RV1106 / RV1126B
4 измерения, 1, 3 или 4 канала Ширина выровнена по 8 байтам Ширина выровнена по 16 байтам
Не 4 измерения Общий размер выровнен по 8 байтам Общий размер выровнен по 16 байтам

3.1.6.2 Процесс нулевого копирования C API

API нулевого копирования использует структуру rknn_tensor_memory, создаваемую и настраиваемую до инференса; после него читаются данные соответствующей памяти. В зависимости от самостоятельного выделения памяти входов, выходов, весов и промежуточных результатов, а также её представления через fd или физический адрес, существуют три типичных процесса на рисунках 3-7–3-9. Красным выделены добавленные для нулевого копирования интерфейсы и структуры, курсивом — структуры, передаваемые между вызовами.

Входная и выходная память выделяется Runtime

Рисунок 3-7. Вызовы API нулевого копирования: внутреннее выделение входной и выходной памяти

Рисунок 3-7. Вызовы API нулевого копирования: внутреннее выделение входной и выходной памяти

На рисунке 3-7 Runtime выделяет входную и выходную память: rknn_create_mem() создаёт rknn_tensor_memory, а rknn_set_io_mem() устанавливает структуры входа и выхода.

Созданная rknn_create_mem() структура содержит файловый дескриптор и физический адрес. RGA использует память NPU: preprocess_rga() обозначает интерфейс RGA, stream_fd — память входного источника RGA, postprocess_cpu() — постобработку на CPU.

Внешнее выделение входной и выходной памяти

Технический рисунок оригинала

Рисунок 3-8. Вызовы API нулевого копирования: внешнее выделение входной и выходной памяти

На рисунке 3-8 при внешнем выделении памяти rknn_create_mem_from_fd() / rknn_create_mem_from_phys() создают rknn_tensor_memory, а rknn_set_io_mem() устанавливает структуры входа и выхода. flush_cache означает, что пользователь должен вызвать подходящий выделенной памяти интерфейс обслуживания выходного кэша.

Внешнее выделение памяти входов, выходов, весов и промежуточных результатов

Технический рисунок оригинала

Рисунок 3-9. Вызовы API нулевого копирования: внешнее выделение входов, выходов, весов и промежуточных результатов

На рисунке 3-9 внешняя память подключается через rknn_create_mem_from_fd() / rknn_create_mem_from_phys(), создающие rknn_tensor_memory. rknn_set_io_mem() задаёт входы и выходы, а rknn_set_weight_mem() / rknn_set_internal_mem() — память весов и промежуточных результатов.

3.1.6.3 Использование C API нулевого копирования

Рассмотрим использование на примере рисунка 3-7 с внутренним выделением памяти входов и выходов:

rknn_query()

Вход:

Запрашивайте атрибуты через RKNN_QUERY_NATIVE_INPUT_ATTR, не RKNN_QUERY_INPUT_ATTR. Требуемая предобработка зависит от полученного fmt (layout). Такой запрос возвращает наиболее эффективные для аппаратуры тип и размещение входа.

Варианты входа rknn_query():

a. RKNN_TENSOR_NCHW: обычно четырёхмерный вход bool или int64. Данные NPU также должны быть организованы в NCHW.

b. RKNN_TENSOR_NHWC: обычно четырёхмерный вход float32/float16/int8/uint8 с 1, 3 или 4 каналами. Данные передаются в NHWC. При pass_through=1 ширину может потребоваться выровнять по stride согласно запрошенному w_stride.

c. RKNN_TENSOR_NC1HWC2: обычно четырёхмерный вход float16/int8, число каналов не равно 1, 3 или 4. При pass_through=0 вход организуется в NHWC, а интерфейс преобразует его на CPU в NC1HWC2. При pass_through=1 требуется NC1HWC2, и пользователь выполняет преобразование самостоятельно.

d. RKNN_TENSOR_UNDEFINED: обычно вход не четырёхмерный. Данные передаются в формате входа модели ONNX. NPU не выполняет обработку mean/std и преобразование размещения.

Если нужна конфигурация входа, отличная от полученной rknn_tensor_attr, структуру можно изменить. Допустимые изменения типов приведены в таблице 3-3. Особенно важно: если запрошен uint8, а передавать нужно float32, размер в rknn_tensor_attr увеличивается в четыре раза, тип меняется на RKNN_TENSOR_FLOAT32. После изменения эффективность аппаратуры уже не оптимальна: интерфейс выполняет преобразование типа на CPU.

Таблица 3-3. Допустимые изменения типа входа

Тип пользователя / тип модели bool int8 float16 int16 int32 Int64
bool Y          
int8   Y        
uint8   Y Y      
float32   Y Y      
float16     Y      
int16       Y    
int32         Y  
Int64           Y

Выход:

Запрашивайте атрибуты через RKNN_QUERY_NATIVE_OUTPUT_ATTR, не RKNN_QUERY_OUTPUT_ATTR. Требуемая постобработка зависит от fmt (layout). Запрос возвращает наиболее эффективные для аппаратуры размещение и тип выхода.

Если четырёхмерный выход нужен в NHWC, используйте RKNN_QUERY_NATIVE_NHWC_OUTPUT_ATTR: так выход NHWC можно получить напрямую.

Варианты выхода rknn_query():

a. RKNN_TENSOR_NC1HWC2: обычно четырёхмерный выход float16/int8. Для NCHW пользователь должен выполнить внешнее преобразование NC1HWC2 → NCHW.

b. RKNN_TENSOR_UNDEFINED: обычно выход не четырёхмерный, тип float16/int8. Внешнее преобразование размещения не требуется.

c. RKNN_TENSOR_NCHW: обычно четырёхмерный выход float16/int8. Внешнее преобразование размещения не требуется.

d. RKNN_TENSOR_NHWC: обычно четырёхмерный выход float16/int8. Такой результат обычно получается при запросе через RKNN_QUERY_NATIVE_NHWC_OUTPUT_ATTR.

Если требуемая конфигурация выхода отличается от rknn_tensor_attr, структуру можно изменить согласно таблицам 3-5 и 3-4. Особенно важно: при запрошенном int8 и требуемом float32 размер увеличивается в четыре раза, тип меняется на RKNN_TENSOR_FLOAT32. После этого аппаратная эффективность уже не оптимальна: преобразование выполняет CPU внутри интерфейса.

Таблица 3-5. Допустимые изменения типа выхода

Тип пользователя / тип модели bool int8 float16 int16 int32 Int64
bool Y          
int8   Y        
uint8            
float32   Y Y      
float16     Y      
int16       Y    
int32         Y  
Int64           Y

Таблица 3-4. Допустимые изменения размещения выхода

Размещение пользователя / модели NC1HWC2 NCHW NHWC UNDEFINE
NC1HWC2 Y      
NCHW Y Y    
NHWC     Y  
UNDEFINE       Y

Поддерживаемые NPU конфигурации выхода нулевого копирования приведены для RK3562/RK3566/RK3568/RK3576/RK3588/RV1126B в таблице 3-6, а для RV1106/RV1106B/RV1103/RV1103B — в таблице 3-7.

Таблица 3-6. Конфигурации выхода NPU нулевого копирования для RK3562/RK3566/RK3568/RK3576/RK3588/RV1126B

Тип модели Тип выхода Размерность выхода Поддерживаемое размещение выхода
Модель int8 int8/float16/float32 4 измерения NCHW/NC1HWC2/NHWC
Модель int8 int8/float16/float32 Не 4 измерения UNDEFINE
Модель float16 float16/float32 4 измерения NCHW/NC1HWC2/NHWC
Модель float16 float16/float32 Не 4 измерения UNDEFINE

Таблица 3-7. Конфигурации выхода NPU нулевого копирования для RV1103/RV1103B/RV1106/RV1106B

Тип модели Тип выхода Размерность выхода Поддерживаемое размещение выхода
Модель int8 int8/float16 4 измерения NCHW/NC1HWC2/NHWC
Модель int8 int8/float16 Не 4 измерения UNDEFINE

rknn_create_mem

Структуру rknn_tensor_memory создают и настраивают до инференса, а затем читают память результатов. Если тип и размещение, запрошенные через RKNN_QUERY_NATIVE_INPUT_ATTR и RKNN_QUERY_NATIVE_OUTPUT_ATTR, не изменяются, выделяйте память размера size_with_stride. При изменении типа или размещения нужен соответствующий размер: например, при переходе выхода int8 → float32 он увеличивается в четыре раза.

rknn_set_io_mem

rknn_set_io_mem() задаёт структуру rknn_tensor_mem с информацией о входной и выходной памяти модели. Как и rknn_init(), её достаточно вызвать один раз в начале, после чего rknn_run() можно выполнять многократно.

Другие статьи по теме → · Учебные маршруты →