На этой странице описываются структуры данных и методы, используемые для эффективной передачи буферов операндов между драйвером и фреймворком.
Во время компиляции модели фреймворк предоставляет драйверу значения константных операндов. В зависимости от времени жизни константного операнда, его значения размещаются либо в векторе HIDL, либо в общем пуле памяти.
- Если время жизни равно
CONSTANT_COPY
, значения размещаются в полеoperandValues
структуры модели. Поскольку значения в векторе HIDL копируются в процессе межпроцессного взаимодействия (IPC), это обычно используется только для хранения небольшого объёма данных, таких как скалярные операнды (например, скаляр активации вADD
) и небольшие тензорные параметры (например, тензор формы вRESHAPE
). - Если время жизни равно
CONSTANT_REFERENCE
, значения размещаются в полеpools
структуры модели. В процессе IPC дублируются только дескрипторы пулов разделяемой памяти, а не копируются необработанные значения. Таким образом, хранение больших объёмов данных (например, весовых параметров в свёртках) эффективнее с помощью пулов разделяемой памяти, чем векторов HIDL.
Во время выполнения модели фреймворк предоставляет драйверу буферы входных и выходных операндов. В отличие от констант времени компиляции, которые могут быть переданы в векторе HIDL, входные и выходные данные выполнения всегда передаются через набор пулов памяти.
Тип данных HIDL hidl_memory
используется как при компиляции, так и при выполнении для представления неотображённого общего пула памяти. Драйвер должен отобразить память соответствующим образом, чтобы сделать её доступной для использования, в соответствии с именем типа данных hidl_memory
. Поддерживаемые имена памяти:
-
ashmem
: Общая память Android. Подробнее см. в разделе «память» . -
mmap_fd
: Общая память, поддерживаемая файловым дескриптором черезmmap
. -
hardware_buffer_blob
: общая память, поддерживаемая буфером AHardwareBuffer в форматеAHARDWARE_BUFFER_FORMAT_BLOB
. Доступно в Neural Networks (NN) HAL 1.2. Подробнее см. в разделе AHardwareBuffer . -
hardware_buffer
: общая память, поддерживаемая общим буфером AHardwareBuffer, который не использует форматAHARDWARE_BUFFER_FORMAT_BLOB
. Аппаратный буфер в режиме, отличном от BLOB, поддерживается только при выполнении модели. Доступно с NN HAL 1.2. Подробнее см. в разделе AHardwareBuffer .
Начиная с версии NN HAL 1.3, NNAPI поддерживает домены памяти, предоставляющие интерфейсы распределения памяти для буферов, управляемых драйвером. Буферы, управляемые драйвером, также могут использоваться в качестве входов и выходов для выполнения. Подробнее см. в разделе Домены памяти .
Драйверы NNAPI должны поддерживать сопоставление имён памяти ashmem
и mmap_fd
. Начиная с версии NN HAL 1.3, драйверы также должны поддерживать сопоставление hardware_buffer_blob
. Поддержка доменов hardware_buffer
и memory общего режима, отличного от BLOB, необязательна.
A HardwareBuffer
AHardwareBuffer — это тип разделяемой памяти, обёртывающий буфер Gralloc . В Android 10 API нейронных сетей (NNAPI) поддерживает использование AHardwareBuffer , что позволяет драйверу выполнять команды без копирования данных, что повышает производительность и энергопотребление приложений. Например, стек HAL камеры может передавать объекты AHardwareBuffer в NNAPI для выполнения задач машинного обучения, используя дескрипторы AHardwareBuffer, сгенерированные API NDK камеры и медиа NDK. Подробнее см. в разделе ANeuralNetworksMemory_createFromAHardwareBuffer
.
Объекты AHardwareBuffer, используемые в NNAPI, передаются драйверу через структуру hidl_memory
с именем hardware_buffer
или hardware_buffer_blob
. Структура hidl_memory
hardware_buffer_blob
представляет только объекты AHardwareBuffer в формате AHARDWAREBUFFER_FORMAT_BLOB
.
Информация, необходимая фреймворку, кодируется в поле hidl_handle
структуры hidl_memory
. Поле hidl_handle
является оболочкой для поля native_handle
, которое кодирует все необходимые метаданные о буфере AHardwareBuffer или Gralloc.
Драйвер должен правильно декодировать предоставленное поле hidl_handle
и получить доступ к памяти, описанной hidl_handle
. При вызове метода getSupportedOperations_1_2
, getSupportedOperations_1_1
или getSupportedOperations
драйвер должен определить, может ли он декодировать предоставленное hidl_handle
и получить доступ к памяти, описанной hidl_handle
. Подготовка модели должна завершиться неудачей, если поле hidl_handle
, используемое для константного операнда, не поддерживается. Выполнение должно завершиться неудачей, если поле hidl_handle
, используемое для входного или выходного операнда выполнения, не поддерживается. Рекомендуется, чтобы драйвер возвращал код ошибки GENERAL_FAILURE
в случае сбоя подготовки или выполнения модели.
Домены памяти
Для устройств под управлением Android 11 и выше NNAPI поддерживает домены памяти, предоставляющие интерфейсы распределения памяти для буферов, управляемых драйвером. Это позволяет передавать данные из собственной памяти устройства между выполнениями, предотвращая ненужное копирование и преобразование данных между последовательными выполнениями на одном драйвере. Этот процесс показан на рисунке 1.
Рисунок 1. Буферизация потока данных с использованием доменов памяти
Функция домена памяти предназначена для тензоров, которые в основном находятся внутри драйвера и не требуют частого доступа на стороне клиента. Примерами таких тензоров являются тензоры состояния в моделях последовательностей. Для тензоров, требующих частого доступа к процессору на стороне клиента, предпочтительно использовать общие пулы памяти.
Для поддержки функции домена памяти реализуйте IDevice::allocate
, чтобы фреймворк мог запрашивать выделение буфера, управляемого драйвером. В процессе выделения фреймворк предоставляет следующие свойства и шаблоны использования для буфера:
-
BufferDesc
описывает требуемые свойства буфера. -
BufferRole
описывает потенциальный шаблон использования буфера как входного или выходного значения подготовленной модели. При выделении буфера можно указать несколько ролей, и выделенный буфер может использоваться только в соответствии с указанными ролями.
Выделенный буфер является внутренним для драйвера. Драйвер может выбрать любое расположение буфера или структуру данных. После успешного выделения буфера клиент драйвера может ссылаться на него или взаимодействовать с ним, используя возвращаемый токен или объект IBuffer
.
Токен из IDevice::allocate
предоставляется при ссылке на буфер как на один из объектов MemoryPool
в структуре Request
выполнения. Чтобы предотвратить попытки процесса получить доступ к буферу, выделенному другим процессом, драйвер должен применять надлежащую проверку при каждом использовании буфера. Драйвер должен проверить, что использование буфера соответствует одной из ролей BufferRole
, предоставленных при выделении, и должен немедленно завершить выполнение, если использование буфера недопустимо.
Объект IBuffer
используется для явного копирования памяти. В определённых ситуациях клиент драйвера должен инициализировать управляемый драйвером буфер из общего пула памяти или скопировать буфер в общий пул памяти. Примеры использования:
- Инициализация тензора состояния
- Кэширование промежуточных результатов
- Резервное выполнение на CPU
Для поддержки этих вариантов использования драйвер должен реализовать IBuffer::copyTo
и IBuffer::copyFrom
с ashmem
, mmap_fd
и hardware_buffer_blob
если он поддерживает выделение доменов памяти. Поддержка режима hardware_buffer
, отличного от BLOB, драйвером необязательна.
При выделении буфера его размеры можно определить из соответствующих операндов модели всех ролей, указанных в BufferRole
, и размеров, указанных в BufferDesc
. С учётом всей информации о размерах буфер может иметь неизвестные размеры или ранг. В таком случае буфер находится в гибком состоянии: размеры фиксированы при использовании в качестве входных данных модели и в динамическом состоянии при использовании в качестве выходных данных модели. Один и тот же буфер может использоваться с различными формами выходных данных в разных исполнениях, и драйвер должен корректно обрабатывать изменение размера буфера.
Домен памяти — это необязательная функция. Драйвер может определить, что он не может поддержать данный запрос на выделение памяти по ряду причин. Например:
- Запрошенный буфер имеет динамический размер.
- Драйвер имеет ограничения памяти, не позволяющие ему обрабатывать большие буферы.
Несколько потоков могут одновременно читать данные из буфера, управляемого драйвером. Одновременный доступ к буферу для записи или чтения/записи не определен, но не должен приводить к сбою службы драйвера или блокировать вызывающую функцию на неопределенный срок. Драйвер может вернуть ошибку или оставить содержимое буфера в неопределенном состоянии.