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