Rockchip · AI & Linux Hub

AI-инференс

RKNN Runtime C API V2.5.0 — Тензоры, выделение памяти и синхронизация

RKNN Runtime C API V2.5.0 — Тензоры, выделение памяти и синхронизация | Полные параметры, примеры и ограничения платформ

· ArmSoM · 12 мин чтения

Платформа и источник

Статья подготовлена по PDF производителя RKNN V2.5.0 и относится только к перечисленным в нём чипам и интерфейсам. Для платы ArmSoM сначала проверьте SoC, драйвер NPU, Toolkit/Runtime, SDK/BSP и версию ядра. Проверки на оборудовании ArmSoM не проводились; примеры и измерения оригинала не являются тестами ArmSoM.

Навигация по серии

5.4. Тензоры и память

Выделение, освобождение, синхронизация тензорной памяти и назначение пользовательской памяти.

rknn_create_mem_from_phys

Для предоставления памяти NPU rknn_create_mem_from_phys создаёт структуру rknn_tensor_mem и возвращает указатель на неё. Физический и виртуальный адреса, размер и сведения о внешней памяти передаются в эту структуру.

API rknn_create_mem_from_phys
Описание Создаёт rknn_tensor_mem и предоставляет память по физическому адресу.
Параметр rknn_context context: объект rknn_context.
Параметр uint64_t phys_addr: физический адрес буфера.
Параметр void *virt_addr: виртуальный адрес буфера, начальный адрес памяти, соответствующей fd.
Параметр uint32_t size: размер буфера.
Возвращаемое значение rknn_tensor_mem*: указатель структуры информации о памяти тензора.

Пример кода:

//suppose we have got buffer information as input_phys, input_virt and size
rknn_tensor_mem* input_mems [1];
input_mems[0] = rknn_create_mem_from_phys(ctx, input_phys, input_virt, size);

rknn_create_mem_from_fd

Для предоставления памяти NPU rknn_create_mem_from_fd создаёт rknn_tensor_mem и возвращает указатель на структуру. В неё передаются файловый дескриптор, логический адрес, размер и сведения о внешней памяти.

API rknn_create_mem_from_fd
Описание Создаёт rknn_tensor_mem и предоставляет память через файловый дескриптор.
Параметр rknn_context context: объект rknn_context.
Параметр int32_t fd: файловый дескриптор буфера.
Параметр void *virt_addr: виртуальный адрес буфера, соответствующий началу fd.
Параметр uint32_t size: размер буфера.
Параметр int32_t offset: смещение, соответствующее файловому дескриптору и виртуальному адресу.
Возвращаемое значение rknn_tensor_mem*: указатель структуры информации о памяти тензора.

Различие оригиналов: В китайском PDF на странице 25 параметр записан как void virt_addr, а * стоит в конце описания; в английском PDF на странице 26 указано void *virt_addr. Таблица сохраняет формулировку соответствующего оригинала. Это расхождение в обозначении типа, а не проверенное объявление API; перед использованием сверьтесь с заголовочным файлом фактического SDK V2.5.0.

Пример кода:

//suppose we have got buffer information as input_fd, input_virt and size
rknn_tensor_mem* input_mems [1];
input_mems[0] = rknn_create_mem_from_fd(ctx, input_fd, input_virt, size, 0);

rknn_create_mem

rknn_create_mem выделяет указанный пользователем объём памяти, непосредственно доступной NPU, и возвращает структуру rknn_tensor_mem.

API rknn_create_mem
Описание Внутри Runtime создаёт rknn_tensor_mem и выделяет память.
Параметр rknn_context context: объект rknn_context.
Параметр uint32_t size: размер буфера.
Возвращаемое значение rknn_tensor_mem*: указатель структуры информации о памяти тензора.

Пример кода:

//suppose we have got buffer size
rknn_tensor_mem* input_mems [1];
input_mems[0] = rknn_create_mem(ctx, size);

rknn_create_mem2

rknn_create_mem2 выделяет непосредственно доступную NPU память заданных пользователем размера и типа, возвращая структуру rknn_tensor_mem.

API rknn_create_mem2
Описание Внутри Runtime создаёт rknn_tensor_mem и выделяет память.
Параметр rknn_context context: объект rknn_context.
Параметр uint64_t size: размер буфера.
Параметр uint64_t alloc_flags: Управляет кэшированием памяти. RKNN_FLAG_MEMORY_CACHEABLE: кэшируемая; RKNN_FLAG_MEMORY_NON_CACHEABLE: некэшируемая.
RKNN_FLAG_MEMORY_FLAGS_DEFAULT совпадает с RKNN_FLAG_MEMORY_CACHEABLE.
RKNN_FLAG_MEMORY_TRY_ALLOC_SRAM: по возможности выделять SRAM; если её недостаточно, остаток выделяется в DRAM.
Возвращаемое значение rknn_tensor_mem*: указатель структуры информации о памяти тензора.

Основное отличие rknn_create_mem2 от rknn_create_mem — параметр alloc_flags, позволяющий указать кэшируемость памяти. rknn_create_mem не предоставляет такого выбора и по умолчанию выделяет кэшируемую память.

Пример кода:

//suppose we have got buffer size
rknn_tensor_mem* input_mems [1];
input_mems[0] = rknn_create_mem2(ctx, size, RKNN_FLAG_MEMORY_NON_CACHEABLE);

rknn_destroy_mem

rknn_destroy_mem уничтожает структуру rknn_tensor_mem. Однако память, выделенную пользователем, необходимо освободить вручную.

API rknn_destroy_mem
Описание Уничтожает структуру rknn_tensor_mem.
Параметр rknn_context context: объект rknn_context.
Параметр rknn_tensor_mem*: указатель структуры информации о памяти тензора.
Возвращаемое значение int: Код ошибки (см. коды состояния).

Пример кода:

rknn_tensor_mem* input_mems [1];
int ret = rknn_destroy_mem(ctx, input_mems[0]);

rknn_set_weight_mem

Если пользователь выделил память весов сети, после инициализации соответствующей rknn_tensor_mem её можно предоставить NPU через rknn_set_weight_mem. Функцию необходимо вызвать до rknn_run.

API rknn_set_weight_mem
Описание Задаёт rknn_tensor_mem со сведениями о памяти весов.
Параметр rknn_context context: объект rknn_context.
Параметр rknn_tensor_mem*: указатель структуры информации о памяти тензора.
Возвращаемое значение int: Код ошибки (см. коды состояния).

Пример кода:

rknn_tensor_mem* weight_mems [1];
int ret = rknn_set_weight_mem(ctx, weight_mems[0]);

rknn_set_internal_mem

Если пользователь выделил память внутренних тензоров сети, после инициализации соответствующей rknn_tensor_mem её можно предоставить NPU через rknn_set_internal_mem. Функцию необходимо вызвать до rknn_run.

API rknn_set_internal_mem
Описание Задаёт rknn_tensor_mem со сведениями о памяти внутренних тензоров сети.
Параметр rknn_context context: объект rknn_context.
Параметр rknn_tensor_mem*: указатель структуры информации о памяти тензора.
Возвращаемое значение int: Код ошибки (см. коды состояния).

Пример кода:

rknn_tensor_mem* internal_tensor_mems [1];
int ret = rknn_set_internal_mem(ctx, internal_tensor_mems[0]);

rknn_set_io_mem

После выделения входной/выходной памяти и инициализации rknn_tensor_mem вызов rknn_set_io_mem разрешает NPU использовать её. Вызывается до rknn_run и относится к zero-copy API. Память может выделять Runtime через rknn_create_mem либо внешний модуль через rknn_create_mem_from_fd/rknn_create_mem_from_phys. Подобно rknn_init, rknn_set_io_mem достаточно вызвать один раз в начале, затем многократно вызывать rknn_run.

API rknn_set_io_mem
Описание Задаёт rknn_tensor_mem со сведениями о памяти входных/выходных тензоров сети.
Параметр rknn_context context: объект rknn_context.
Параметр rknn_tensor_mem*: указатель структуры информации о памяти тензора.
Параметр rknn_tensor_attr *: атрибуты буфера входного или выходного тензора.
Возвращаемое значение int: Код ошибки (см. коды состояния).

Примечания по использованию:

  1. Получите rknn_tensor_attr через rknn_query. Для zero-copy используйте RKNN_QUERY_NATIVE_INPUT_ATTR/RKNN_QUERY_NATIVE_OUTPUT_ATTR, а не RKNN_QUERY_INPUT_ATTR/RKNN_QUERY_OUTPUT_ATTR: это даёт наиболее эффективные для NPU компоновку и тип.
  1. Если тип и компоновка не меняются, выделяйте size_with_stride байт. При изменении выделяйте новый объём: например, переход выхода int8 → float32 требует четырёхкратного размера, n_elems * sizeof(float).
  1. Вход должен соответствовать запрошенной или изменённой компоновке, например NHWC. При pass_through=1 может потребоваться выравнивание ширины по w_stride.
  1. После инференса считайте выходную память из rknn_tensor_mem. Для внешней памяти также требуется сброс кэша через интерфейс соответствующего типа памяти.

Пример кода:

// Zero-copy API: query native attributes -> modify type/layout -> allocate memory -> set input/output memory
rknn_tensor_attr input_attrs[1], output_attrs[1];
rknn_tensor_mem* input_mems[1];
rknn_tensor_mem* output_mems[1];

memset(input_attrs, 0, sizeof(input_attrs));
input_attrs[0].index = 0;
rknn_query(ctx, RKNN_QUERY_NATIVE_INPUT_ATTR, &(input_attrs[0]), sizeof(rknn_tensor_attr));
// The default input type is int8 (normalize and quantize need to be computed outside);
// if uint8 is set, normalize and quantize will be fused into the NPU
input_attrs[0].type = RKNN_TENSOR_UINT8;
// The default fmt is NHWC, and the NPU only supports NHWC input in zero-copy mode
input_attrs[0].fmt = RKNN_TENSOR_NHWC;
input_mems[0] = rknn_create_mem(ctx, input_attrs[0].size_with_stride);
// Copy input data to input_mems[0]->virt_addr ...

memset(output_attrs, 0, sizeof(output_attrs));
output_attrs[0].index = 0;
rknn_query(ctx, RKNN_QUERY_NATIVE_OUTPUT_ATTR, &(output_attrs[0]), sizeof(rknn_tensor_attr));
// The default output type depends on the model; float32 is required here to compute top5
output_attrs[0].type = RKNN_TENSOR_FLOAT32;
output_mems[0] = rknn_create_mem(ctx, output_attrs[0].n_elems * sizeof(float));

// Set input/output memory
ret = rknn_set_io_mem(ctx, input_mems[0], &input_attrs[0]);
ret = rknn_set_io_mem(ctx, output_mems[0], &output_attrs[0]);

// Run inference
ret = rknn_run(ctx, NULL);
// Read the output result from output_mems[0]->virt_addr ...

rknn_mem_sync

Память rknn_create_mem по умолчанию кэшируемая. При одновременном доступе CPU и NPU к такой памяти поведение кэша может нарушать согласованность данных. Этот интерфейс синхронизирует кэшируемую память, обеспечивая согласованность данных, доступных CPU и NPU.

API rknn_mem_sync
Описание Синхронизирует данные кэша CPU и DDR.
Параметр rknn_context context: объект rknn_context.
Параметр rknn_tensor_mem* mem: указатель структуры информации о памяти тензора.
Параметр rknn_mem_sync_mode mode: режим обновления CPU-кэша и DDR. RKNN_MEMORY_SYNC_TO_DEVICE — записывает данные CPU-кэша в DDR, обычно после записи CPU и перед доступом NPU к той же памяти. RKNN_MEMORY_SYNC_FROM_DEVICE — синхронизирует DDR с CPU-кэшем, обычно после записи NPU: делает кэш недействительным, чтобы при следующем доступе CPU повторно прочитал DDR. RKNN_MEMORY_SYNC_BIDIRECTIONAL — записывает CPU-кэш в DDR и заставляет CPU заново читать DDR.
Возвращаемое значение int: Код ошибки (см. коды состояния).

Пример кода:

ret =rknn_mem_sync(ctx, &outputs[0].mem,
RKNN_MEMORY_SYNC_FROM_DEVICE);
if (ret < 0) {
   fprintf(stderr, " rknn_mem_sync error! ret=%d\n", ret);
   return -1;
}

Источник и редакционные примечания

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

Оговорки оригинала: номера глав и примерные журналы сохранены; ранние версии SDK в журналах не являются версией этой статьи. Runtime переходит от главы 3 к 5 и повторяет номера запросов. Таблица rknn_init называет extend отключённым, но далее описывает user_key и общие веса. Текст о нативном динамическом выходе указывает RKNN_QUERY_CURRENT_OUTPUT_ATTR, пример — RKNN_QUERY_CURRENT_NATIVE_OUTPUT_ATTR. Некоторые описания native NHWC/NC1HWC2 смешивают вход и выход. Проверяйте фактические заголовки V2.5.0 и целевой SDK: поведение не подтверждено тестами. Ссылка RK2118 в оригинале называется RKNN3_RK2118_Quick_Start.pdf, а в текущем каталоге находится руководство RKNN2.

RKNN-Toolkit2 · ArmSoM documentation

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