На этой странице описываются структуры данных и методы, используемые для эффективной передачи буферов операндов между драйвером и фреймворком.
Во время компиляции модели фреймворк предоставляет значения константных операндов драйверу. В зависимости от времени жизни константного операнда его значения располагаются либо в векторе 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
. Аппаратный буфер в режиме non-BLOB поддерживается только при выполнении модели. Доступно с NN HAL 1.2. Для получения более подробной информации см. AHardwareBuffer .
Начиная с NN HAL 1.3, NNAPI поддерживает домены памяти, которые предоставляют интерфейсы распределителя для буферов, управляемых драйвером. Буферы, управляемые драйвером, также могут использоваться как входы или выходы выполнения. Для получения более подробной информации см. Домены памяти .
Драйверы NNAPI должны поддерживать отображение имен памяти ashmem
и mmap_fd
. Начиная с NN HAL 1.3, драйверы также должны поддерживать отображение hardware_buffer_blob
. Поддержка общего режима не-BLOB hardware_buffer
и доменов памяти является необязательной.
AHardwareBuffer
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
если он поддерживает выделение домена памяти. Драйверу необязательно поддерживать режим non-BLOB hardware_buffer
.
Во время выделения буфера размеры буфера могут быть выведены из соответствующих операндов модели всех ролей, указанных BufferRole
, и размеров, предоставленных в BufferDesc
. При объединении всей размерной информации буфер может иметь неизвестные размеры или ранг. В таком случае буфер находится в гибком состоянии, где размеры фиксированы при использовании в качестве входных данных модели и в динамическом состоянии при использовании в качестве выходных данных модели. Один и тот же буфер может использоваться с различными формами выходных данных в различных исполнениях, и драйвер должен правильно обрабатывать изменение размера буфера.
Домен памяти — это необязательная функция. Драйвер может определить, что он не может поддерживать данный запрос выделения по ряду причин. Например:
- Запрошенный буфер имеет динамический размер.
- Драйвер имеет ограничения памяти, не позволяющие ему обрабатывать большие буферы.
Возможно, что несколько разных потоков будут одновременно читать из буфера, управляемого драйвером. Одновременный доступ к буферу для записи или чтения/записи не определен, но он не должен приводить к сбою службы драйвера или блокировать вызывающую сторону на неопределенный срок. Драйвер может вернуть ошибку или оставить содержимое буфера в неопределенном состоянии.