На этой странице описываются структуры данных и методы, используемые для эффективной передачи буферов операндов между драйвером и фреймворком.
Во время компиляции модели фреймворк предоставляет драйверу значения константных операндов. В зависимости от времени жизни константного операнда, его значения размещаются либо в векторе 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 . С учётом всей информации о размерах буфер может иметь неизвестные размеры или ранг. В таком случае буфер находится в гибком состоянии: размеры фиксированы при использовании в качестве входных данных модели и в динамическом состоянии при использовании в качестве выходных данных модели. Один и тот же буфер может использоваться с различными формами выходных данных в разных исполнениях, и драйвер должен корректно обрабатывать изменение размера буфера.
Домен памяти — это необязательная функция. Драйвер может определить, что он не может поддержать данный запрос на выделение памяти по ряду причин. Например:
- Запрошенный буфер имеет динамический размер.
- Драйвер имеет ограничения памяти, не позволяющие ему обрабатывать большие буферы.
Несколько потоков могут одновременно читать данные из буфера, управляемого драйвером. Одновременный доступ к буферу для записи или чтения/записи не определен, но не должен приводить к сбою службы драйвера или блокировать вызывающую функцию на неопределенный срок. Драйвер может вернуть ошибку или оставить содержимое буфера в неопределенном состоянии.