メモリプール

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

モデルのコンパイル時に、フレームワークは定数オペランドの値をドライバーに提供します。定数オペランドの有効期間に応じて、その値はHIDLベクトルまたは共有メモリプールのいずれかに配置されます。

  • 有効期間がCONSTANT_COPYの場合、値はモデル構造のoperandValuesフィールドにあります。 HIDLベクトルの値は、プロセス間通信(IPC)中にコピーされるため、これは通常、スカラーオペランド(たとえば、 ADDのアクティブ化スカラー)や小さなテンソルパラメーター(たとえば、 RESHAPEの形状テンソル)。
  • 有効期間がCONSTANT_REFERENCEの場合、値はモデル構造のpoolsフィールドにあります。共有メモリプールのハンドルのみが、生の値をコピーするのではなく、IPC中に複製されます。したがって、HIDLベクトルよりも、共有メモリプールを使用して、大量のデータ(たとえば、畳み込みの重みパラメーター)を保持する方が効率的です。

モデルの実行時に、フレームワークは入力オペランドと出力オペランドのバッファーをドライバーに提供します。 HIDLベクトルで送信される可能性のあるコンパイル時定数とは異なり、実行の入力データと出力データは、常にメモリプールのコレクションを介して通信されます。

HIDLデータ型hidl_memoryは、マップされていない共有メモリプールを表すために、コンパイルと実行の両方で使用されます。ドライバは、 hidl_memoryデータ型の名前に基づいてメモリを使用できるように、それに応じてメモリをマップする必要があります。サポートされているメモリ名は次のとおりです。

  • ashmem :Android共有メモリ。詳細については、メモリを参照してください。
  • mmap_fdmmapを介してファイル記述子でバックアップされた共有メモリ。
  • hardware_buffer_blob :AHARDWARE_BUFFER_FORMAT_BLOB形式のAHARDWARE_BUFFER_FORMAT_BLOBによってバックアップされた共有メモリ。 Neural Networks(NN)HAL1.2から入手できます。詳細については、 AHardwareBufferを参照してください。
  • hardware_buffer :フォーマットAHARDWARE_BUFFER_FORMAT_BLOBを使用しない一般的なAHardwareBufferによってバックアップされる共有メモリ。非BLOBモードのハードウェアバッファは、モデルの実行でのみサポートされます。NNHAL1.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では、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フィールドはnative_handleをラップします。これは、AHardwareBufferまたはGrallocバッファーに関する必要なすべてのメタデータをエンコードします。

ドライバは、提供された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提供された次元から推定できます。すべてのディメンション情報を組み合わせると、バッファのディメンションまたはランクが不明になる可能性があります。このような場合、バッファは、モデル入力として使用される場合は寸法が固定され、モデル出力として使用される場合は動的状態になる柔軟な状態になります。同じバッファーをさまざまな実行でさまざまな形状の出力で使用でき、ドライバーはバッファーのサイズ変更を適切に処理する必要があります。

メモリドメインはオプション機能です。ドライバーは、いくつかの理由で、特定の割り当て要求をサポートできないと判断できます。例えば:

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

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