メモリプール

このページでは、ドライバーとフレームワーク間でオペランド バッファーを効率的に通信するために使用されるデータ構造とメソッドについて説明します。

モデルのコンパイル時に、フレームワークは定数オペランドの値をドライバーに提供します。定数オペランドの有効期間に応じて、その値は 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 : AHARDWARE_BUFFER_FORMAT_BLOB形式の AHardwareBuffer によってバックアップされる共有メモリ。ニューラル ネットワーク (NN) HAL 1.2 から利用可能です。詳細については、 「 AHardwareBuffer 」を参照してください。
  • hardware_buffer : AHARDWARE_BUFFER_FORMAT_BLOB形式を使用しない一般的な AHardwareBuffer によってバックアップされる共有メモリ。非 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およびメモリ ドメインのサポートはオプションです。

Aハードウェアバッファ

AHardwareBuffer は、 Gralloc バッファをラップする共有メモリの一種です。 Android 10 では、Neural Networks API (NNAPI) がAHardwareBufferの使用をサポートし、ドライバーがデータをコピーせずに実行できるようになり、アプリのパフォーマンスと消費電力が向上します。たとえば、カメラ HAL スタックは、カメラ NDK およびメディア NDK API によって生成された AHardwareBuffer ハンドルを使用して、機械学習ワークロードの AHardwareBuffer オブジェクトを NNAPI に渡すことができます。詳細については、 ANeuralNetworksMemory_createFromAHardwareBufferを参照してください。

NNAPI で使用される AHardwareBuffer オブジェクトは、 hardware_bufferまたはhardware_buffer_blobという名前のhidl_memory構造体を通じてドライバーに渡されます。 hidl_memory構造体hardware_buffer_blob AHARDWAREBUFFER_FORMAT_BLOB形式の AHardwareBuffer オブジェクトのみを表します。

フレームワークに必要な情報は、 hidl_memory構造体のhidl_handleフィールドにエンコードされます。 hidl_handleフィールドは、AHardwareBuffer または Gralloc バッファーに関する必要なメタデータをすべてエンコードするnative_handleハンドルをラップします。

ドライバーは、提供されたhidl_handleフィールドを適切にデコードし、 hidl_handleで記述されたメモリにアクセスする必要があります。 getSupportedOperations_1_2getSupportedOperations_1_1 、またはgetSupportedOperationsメソッドが呼び出されるとき、ドライバーは、提供されたhidl_handleデコードして、 hidl_handleで記述されたメモリにアクセスできるかどうかを検出する必要があります。定数オペランドに使用されるhidl_handleフィールドがサポートされていない場合、モデルの準備は失敗する必要があります。実行の入力オペランドまたは出力オペランドに使用されるhidl_handleフィールドがサポートされていない場合、実行は失敗する必要があります。モデルの準備または実行が失敗した場合、ドライバーはGENERAL_FAILUREエラー コードを返すことが推奨されます。

メモリドメイン

Android 11 以降を実行しているデバイスの場合、NNAPI はドライバー管理のバッファーにアロケーター インターフェイスを提供するメモリ ドメインをサポートします。これにより、複数の実行間でデバイスのネイティブ メモリを渡すことが可能になり、同じドライバーでの連続した実行間での不必要なデータのコピーや変換が抑制されます。このフローを図 1 に示します。

メモリドメインがある場合とない場合のバッファデータフロー

図 1.メモリ ドメインを使用したバッファ データ フロー

メモリ ドメイン機能は、主にドライバーの内部にあり、クライアント側で頻繁にアクセスする必要のないテンソルを対象としています。このようなテンソルの例には、シーケンス モデルの状態テンソルが含まれます。クライアント側で頻繁に CPU アクセスを必要とするテンソルの場合は、共有メモリ プールを使用することをお勧めします。

メモリ ドメイン機能をサポートするには、 IDevice::allocateを実装して、フレームワークがドライバー管理のバッファ割り当てを要求できるようにします。割り当て中に、フレームワークはバッファーに次のプロパティと使用パターンを提供します。

  • BufferDescバッファーの必要なプロパティを説明します。
  • BufferRole準備されたモデルの入力または出力としてのバッファーの潜在的な使用パターンを記述します。バッファ割り当て時に複数のロールを指定でき、割り当てられたバッファは指定されたロールとしてのみ使用できます。

割り当てられたバッファはドライバーの内部にあります。ドライバーは、任意のバッファーの場所またはデータ レイアウトを選択できます。バッファーが正常に割り当てられると、ドライバーのクライアントは、返されたトークンまたはIBufferオブジェクトを使用してバッファーを参照したり、バッファーと対話したりできます。

IDevice::allocateのトークンは、実行のRequest構造内のMemoryPoolオブジェクトの 1 つとしてバッファーを参照するときに提供されます。プロセスが別のプロセスに割り当てられたバッファにアクセスしようとするのを防ぐために、ドライバはバッファを使用するたびに適切な検証を適用する必要があります。ドライバーは、バッファーの使用法が割り当て中に提供されたBufferRoleロールの 1 つであることを検証する必要があり、使用法が不正な場合はただちに実行を失敗させる必要があります。

IBufferオブジェクトは、明示的なメモリ コピーに使用されます。特定の状況では、ドライバーのクライアントはドライバー管理のバッファーを共有メモリ プールから初期化するか、バッファーを共有メモリ プールにコピーする必要があります。ユースケースの例は次のとおりです。

  • 状態テンソルの初期化
  • 中間結果のキャッシュ
  • CPU でのフォールバック実行

これらのユースケースをサポートするには、ドライバーがメモリ ドメインの割り当てをサポートしている場合は、 ashmemmmap_fd 、およびhardware_buffer_blobを使用してIBuffer::copyToおよびIBuffer::copyFromを実装する必要があります。ドライバーが非 BLOB モードhardware_bufferをサポートするかどうかはオプションです。

バッファ割り当て中に、バッファの次元は、 BufferRoleで指定されたすべてのロールの対応するモデル オペランドと、 BufferDescで指定された次元から推定できます。すべての次元情報を組み合わせると、バッファーの次元やランクが不明になる可能性があります。このような場合、バッファーは、モデル入力として使用される場合は次元が固定された柔軟な状態になり、モデル出力として使用される場合は動的状態になります。同じバッファーを異なる実行で異なる形状の出力で使用することができ、ドライバーはバッファーのサイズ変更を適切に処理する必要があります。

メモリ ドメインはオプションの機能です。ドライバーは、さまざまな理由により、特定の割り当て要求をサポートできないと判断する場合があります。例えば:

  • 要求されたバッファには動的なサイズがあります。
  • ドライバーにはメモリ制約があるため、大きなバッファーを処理できません。

複数の異なるスレッドがドライバー管理のバッファーから同時に読み取ることが可能です。書き込みまたは読み取り/書き込みのためにバッファに同時にアクセスすることは未定義ですが、ドライバー サービスをクラッシュさせたり、呼び出し元を無期限にブロックしたりしてはなりません。ドライバーはエラーを返したり、バッファーの内容を不定の状態のままにしたりすることがあります。