メモリプール

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

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

モデルのコンパイル時に、フレームワークは定数オペランドの値をドライバーに提供します。定数オペランドの有効期間に応じて、その値は 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_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 でのフォールバック実行

これらの使用例をサポートするには、メモリー ドメインの割り当てをサポートする場合、ドライバーはIBuffer::copyToおよびIBuffer::copyFromashmemmmap_fd 、およびhardware_buffer_blobと共に実装する必要があります。ドライバーが非 BLOB モードのhardware_bufferをサポートすることはオプションです。

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

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

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

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