Пулы памяти

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

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

  • Если время существования равно CONSTANT_COPY , значения находятся в поле operandValues ​​структуры модели. Поскольку значения в векторе HIDL копируются во время межпроцессного взаимодействия (IPC), это обычно используется только для хранения небольшого количества данных, таких как скалярные операнды (например, скаляр активации в ADD ) и небольшие тензорные параметры (например, тензор формы в RESHAPE ).
  • Если время существования равно CONSTANT_REFERENCE , значения находятся в поле pools структуры модели. Во время IPC дублируются только дескрипторы пулов общей памяти, а не необработанные значения. Поэтому эффективнее хранить большой объем данных (например, весовые параметры в свертках) с использованием пулов общей памяти, чем 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 не является обязательной.

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

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

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

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

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