Пулы памяти

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

Во время компиляции модели платформа предоставляет драйверу значения постоянных операндов. В зависимости от времени жизни константного операнда его значения располагаются либо в векторе 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 и доменов памяти, отличного от режима BLOB, является необязательной.

Аппаратный буфер

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 используется для явного копирования памяти. В определенных ситуациях клиент драйвера должен инициализировать управляемый драйвером буфер из пула общей памяти или скопировать буфер в пул общей памяти. Примеры вариантов использования включают в себя:

  • Инициализация тензора состояния
  • Кэширование промежуточных результатов
  • Резервное выполнение на ЦП

Для поддержки этих вариантов использования драйвер должен реализовать IBuffer::copyTo и IBuffer::copyFrom с ashmem , mmap_fd и hardware_buffer_blob , если он поддерживает выделение домена памяти. Драйвер необязательно поддерживает hardware_buffer в режиме, отличном от BLOB.

Во время выделения буфера размеры буфера можно вывести из соответствующих операндов модели всех ролей, указанных в BufferRole , и размеров, предоставленных в BufferDesc . При объединении всей информации о измерениях буфер может иметь неизвестные измерения или ранг. В таком случае буфер находится в гибком состоянии, когда размеры фиксированы при использовании в качестве входных данных модели, и в динамическом состоянии при использовании в качестве выходных данных модели. Один и тот же буфер может использоваться с разными формами выходных данных в разных исполнениях, и драйвер должен правильно обрабатывать изменение размера буфера.

Домен памяти является дополнительной функцией. Драйвер может определить, что он не может поддерживать данный запрос на выделение по ряду причин. Например:

  • Запрошенный буфер имеет динамический размер.
  • Драйвер имеет ограничения памяти, не позволяющие ему обрабатывать большие буферы.

Несколько разных потоков могут одновременно читать данные из буфера, управляемого драйвером. Доступ к буферу одновременно для записи или чтения/записи не определен, но он не должен вызывать сбой в работе службы драйвера или блокировать вызывающую сторону на неопределенный срок. Драйвер может вернуть ошибку или оставить содержимое буфера в неопределенном состоянии.