內存池

本頁描述了用於在驅動程序和框架之間有效地通信操作數緩衝區的數據結構和方法。

在模型編譯時,框架將常量操作數的值提供給驅動程序。根據常量操作數的生命週期,其值位於 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 :由 AHardwareBuffer 支持的共享內存,格式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 驅動程序必須支持ashmemmmap_fd內存名稱的映射。從 NN HAL 1.3 開始,驅動程序還必須支持hardware_buffer_blob的映射。對一般非 BLOB 模式hardware_buffer和內存域的支持是可選的。

A硬件緩衝區

AHardwareBuffer 是一種包裝Gralloc 緩衝區的共享內存。在 Android 10 中,神經網絡 API (NNAPI) 支持使用AHardwareBuffer ,允許驅動程序在不復制數據的情況下執行執行,從而提高應用程序的性能和功耗。例如,相機 HAL 堆棧可以使用相機 NDK 和媒體 NDK API 生成的 AHardwareBuffer 句柄將 AHardwareBuffer 對像傳遞給 NNAPI 以進行機器學習工作負載。有關詳細信息,請參閱ANeuralNetworksMemory_createFromAHardwareBuffer

NNAPI 中使用的 AHardwareBuffer 對象通過名為hardware_bufferhardware_buffer_blobhidl_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_1getSupportedOperations方法時,驅動程序應該檢測它是否可以解碼提供的hidl_handle並訪問hidl_handle描述的內存。如果不支持用於常量操作數的hidl_handle字段,則模型準備必須失敗。如果不支持用於執行的輸入或輸出操作數的hidl_handle字段,則執行必須失敗。如果模型準備或執行失敗,建議驅動程序返回GENERAL_FAILURE錯誤代碼。

內存域

對於運行 Android 11 或更高版本的設備,NNAPI 支持為驅動程序管理的緩衝區提供分配器接口的內存域。這允許跨執行傳遞設備本機內存,抑制在同一驅動程序上連續執行之間不必要的數據複製和轉換。此流程如圖 1 所示。

帶和不帶內存域的緩衝區數據流
圖 1.使用內存域的緩衝區數據流

內存域功能適用於大部分位於驅動程序內部且不需要在客戶端頻繁訪問的張量。這種張量的例子包括序列模型中的狀態張量。對於需要在客戶端頻繁訪問 CPU 的張量,最好使用共享內存池。

要支持內存域功能,請實現IDevice::allocate以允許框架請求驅動程序管理的緩衝區分配。在分配期間,框架為緩衝區提供以下屬性和使用模式:

  • BufferDesc描述了緩衝區所需的屬性。
  • BufferRole將緩衝區的潛在使用模式描述為準備好的模型的輸入或輸出。緩衝區分配時可以指定多個角色,分配的緩衝區只能作為指定的角色使用。

分配的緩衝區在驅動程序內部。驅動程序可以選擇任何緩衝區位置或數據佈局。當緩衝區分配成功後,驅動程序的客戶端可以使用返回的令牌或IBuffer對象來引用緩衝區或與緩衝區進行交互。

當引用緩衝區作為執行的Request結構中的MemoryPool對象之一時,會提供來自IDevice::allocate的令牌。為了防止一個進程試圖訪問在另一個進程中分配的緩衝區,驅動程序必須在每次使用緩衝區時應用適當的驗證。驅動程序必須驗證緩衝區使用是分配期間提供的BufferRole角色之一,如果使用非法,則必須立即執行失敗。

IBuffer對像用於顯式內存複製。在某些情況下,驅動程序的客戶端必須從共享內存池初始化驅動程序管理的緩衝區或將緩衝區復製到共享內存池。示例用例包括:

  • 狀態張量的初始化
  • 緩存中間結果
  • CPU 上的後備執行

為了支持這些用例,如果驅動程序支持內存域分配,則必須使用ashmemmmap_fdhardware_buffer_blob實現IBuffer::copyToIBuffer::copyFrom 。驅動程序支持非 BLOB 模式hardware_buffer是可選的。

在緩衝區分配過程中,緩衝區的維度可以從 BufferRole 指定的所有角色的相應模型操作數以及BufferRole中提供的維度BufferDesc 。結合所有維度信息,緩衝區可能具有未知的維度或等級。在這種情況下,緩衝器處於靈活狀態,當用作模型輸入時,尺寸是固定的,而當用作模型輸出時,緩衝器處於動態狀態。相同的緩衝區可以在不同的執行中用於不同形狀的輸出,並且驅動程序必須正確處理緩衝區大小調整。

內存域是一個可選功能。由於多種原因,驅動程序可以確定它不能支持給定的分配請求。例如:

  • 請求的緩衝區具有動態大小。
  • 驅動程序有內存限制,阻止它處理大緩衝區。

多個不同的線程可以同時從驅動程序管理的緩衝區中讀取。同時訪問緩衝區進行寫或讀/寫是未定義的,但它不能使驅動程序服務崩潰或無限期地阻塞調用者。驅動程序可以返回錯誤或使緩衝區的內容處於不確定狀態。