このページでは、ドライバとフレームワークの間でオペランドバッファを効率的に通信するために使用されるデータ構造とメソッドについて説明します。
モデルのコンパイル時に、フレームワークは定数オペランドの値をドライバーに提供します。定数オペランドの有効期間に応じて、その値は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形式の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_2
、 getSupportedOperations_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でのフォールバック実行
これらのユースケースをサポートするには、ドライバがメモリドメイン割り当てをサポートしている場合、 ashmem
、 mmap_fd
、およびhardware_buffer_blob
を使用してIBuffer::copyTo
およびIBuffer::copyFrom
を実装する必要があります。ドライバーが非BLOBモードのhardware_buffer
をサポートすることはオプションです。
バッファーの割り当て中に、バッファーの次元は、 BufferRole
BufferDesc
提供された次元から推定できます。すべてのディメンション情報を組み合わせると、バッファのディメンションまたはランクが不明になる可能性があります。このような場合、バッファは、モデル入力として使用される場合は寸法が固定され、モデル出力として使用される場合は動的状態になる柔軟な状態になります。同じバッファーをさまざまな実行でさまざまな形状の出力で使用でき、ドライバーはバッファーのサイズ変更を適切に処理する必要があります。
メモリドメインはオプション機能です。ドライバーは、いくつかの理由で、特定の割り当て要求をサポートできないと判断できます。例えば:
- 要求されたバッファのサイズは動的です。
- ドライバにはメモリの制約があり、大きなバッファを処理できません。
複数の異なるスレッドがドライバー管理バッファーから同時に読み取る可能性があります。書き込みまたは読み取り/書き込みのために同時にバッファーにアクセスすることは定義されていませんが、ドライバーサービスをクラッシュさせたり、呼び出し元を無期限にブロックしたりしてはなりません。ドライバはエラーを返すか、バッファの内容を不確定な状態のままにする可能性があります。