記憶體集區

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

在模型編譯期間,架構會將常數運算元的數值提供給驅動程式。視常數運算元的生命週期而定,其值會位於 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 驅動程式必須支援 ashmemmmap_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_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 上執行備用作業

如要支援這些用途,驅動程式必須實作 IBuffer::copyToIBuffer::copyFrom,並搭配 ashmemmmap_fdhardware_buffer_blob (如果驅動程式支援記憶體網域分配)。驅動程式可選擇是否支援非 BLOB 模式 hardware_buffer

在緩衝區分配期間,緩衝區的維度可從 BufferRole 指定的所有角色對應模型運算元,以及 BufferDesc 中提供的維度推斷而來。結合所有維度資訊後,緩衝區可能會有不明維度或秩。在這種情況下,緩衝區處於彈性狀態,當做模型輸入內容使用時,維度是固定的,當做模型輸出內容使用時,維度則是動態的。在不同的執行作業中,可使用相同緩衝區搭配不同形狀的輸出內容,且驅動程式必須正確處理緩衝區大小調整作業。

記憶體網域是選用功能。基於多種原因,驅動程式可能會判斷無法支援特定配置要求。例如:

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

多個不同執行緒可能會同時從驅動程式管理的緩衝區讀取資料。同時存取緩衝區進行寫入或讀取/寫入作業的行為未定義,但不得導致驅動程式服務當機,或無限期封鎖呼叫端。驅動程式可能會傳回錯誤,或將緩衝區的內容保留在不確定的狀態。