記憶體集區

本頁說明用於在驅動程式和架構之間有效通訊運算元緩衝區的資料結構和方法。

在模型編譯期間,架構會將常數運算元的值提供給驅動程式。視常數運算元的生命週期而定,其值可能位於 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 和記憶體網域。

AHardwareBuffer

AHardwareBuffer 是包裝 Gralloc 緩衝區的共用記憶體類型。在 Android 10 中,Neural Networks API (NNAPI) 支援使用 AHardwareBuffer,讓驅動程式無需複製資料即可執行執行作業,進而提升應用程式的效能和耗電量。舉例來說,相機 HAL 堆疊可以將 AHardwareBuffer 物件傳遞至 NNAPI,以便使用 AHardwareBuffer 處理機器學習工作負載 (相機 NDK 和媒體 NDK API 產生) 進行機器學習工作負載。詳情請參閱 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 指定的所有角色和 BufferDesc 中提供的維度,推算出緩衝區的尺寸。整合所有維度資訊後,緩衝區可能有不明的維度或排名。在這類情況下,緩衝區處於彈性狀態,當做為模型輸入使用時,尺寸會固定,而做為模型輸出內容使用時,則是以動態狀態呈現。同一個緩衝區可以在不同執行作業中搭配不同的輸出形狀使用,而且驅動程式必須正確處理緩衝區大小調整作業。

記憶體網域為選用功能。驅動程式可能會基於幾種原因,判斷無法支援指定的分配要求。舉例來說:

  • 要求的緩衝區具有動態大小。
  • 驅動程式受限於記憶體限制,無法處理大型緩衝區。

好幾個不同的執行緒可以同時從驅動程式管理的緩衝區讀取資料。同時存取緩衝區以寫入或讀取/寫入並非定義,但不得造成驅動程式服務當機或無限期封鎖呼叫端。驅動程式可能會傳回錯誤,或讓緩衝區的內容處於不定狀態。