本頁描述了用於在驅動程式和框架之間有效地通訊操作數緩衝區的資料結構和方法。
在模型編譯時,框架向驅動程式提供常數運算元的值。根據常數運算元的生命週期,其值位於 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
:由通用 AHardwareBuffer 支援的共享內存,不使用AHARDWARE_BUFFER_FORMAT_BLOB
格式。非 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 是一種包裝Gralloc 緩衝區的共享記憶體。在 Android 10 中,神經網路 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
物件來引用緩衝區或與緩衝區互動。
當緩衝區引用為執行的Request
結構中的MemoryPool
物件之一時,會提供來自IDevice::allocate
的令牌。為了防止進程嘗試存取另一個進程中分配的緩衝區,驅動程式必須在每次使用緩衝區時應用適當的驗證。驅動程式必須驗證緩衝區使用情況是分配期間提供的BufferRole
角色之一,如果使用非法,則必須立即執行失敗。
IBuffer
物件用於明確記憶體複製。在某些情況下,驅動程式的用戶端必須從共享記憶體池初始化驅動程式管理的緩衝區或將緩衝區複製到共享記憶體池。範例用例包括:
- 狀態張量的初始化
- 快取中間結果
- CPU 上的回退執行
為了支援這些用例,如果驅動程式支援記憶體域分配,則必須使用ashmem
、 mmap_fd
和hardware_buffer_blob
實作IBuffer::copyTo
和IBuffer::copyFrom
。驅動程式可以選擇支援非 BLOB 模式hardware_buffer
。
在緩衝區分配期間,緩衝區的維度可以從BufferRole
指定的所有角色的相應模型操作數以及BufferDesc
中提供的維度推斷出來。將所有維度資訊組合起來後,緩衝區可能具有未知的維度或等級。在這種情況下,緩衝器處於靈活狀態,其中當用作模型輸入時尺寸是固定的,而當用作模型輸出時則處於動態狀態。相同的緩衝區可以在不同的執行中與不同形狀的輸出一起使用,並且驅動程式必須正確處理緩衝區大小調整。
記憶體域是一個可選功能。驅動程式可以出於多種原因確定它無法支援給定的分配請求。例如:
- 請求的緩衝區具有動態大小。
- 驅動程式有記憶體限制,無法處理大緩衝區。
多個不同的執行緒可以同時從驅動程式管理的緩衝區讀取資料。同時存取緩衝區進行寫入或讀取/寫入是未定義的,但它不得使驅動程式服務崩潰或無限期地阻止呼叫者。驅動程式可以傳回錯誤或使緩衝區的內容處於不確定狀態。