記憶體集區

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

在模型編譯期間,架構會將常數運算元的值提供給驅動程式。視常數運算子的生命週期而定,其值會位於 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 格式的 AHardwareBuffer 支援的共用記憶體。可透過神經網路 (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 堆疊可以將 AHardwareBuffer 物件傳遞至 NNAPI,以便使用由相機 NDK 和媒體 NDK API 產生的 AHardwareBuffer 句柄,執行機器學習工作負載。詳情請參閱 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 中提供的尺寸,推斷緩衝區的尺寸。結合所有維度資訊後,緩衝區可能會有不明的維度或秩。在這種情況下,緩衝區處於彈性狀態,當做為模型輸入時,維度會固定,當做為模型輸出時,則處於動態狀態。在不同執行作業中,同一個緩衝區可搭配不同形狀的輸出內容使用,且驅動程式必須妥善處理緩衝區大小調整。

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

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

多個不同的執行緒可以同時從驅動程式管理的緩衝區讀取資料。同時存取緩衝區進行寫入或讀取/寫入操作是不允許的,但不得導致驅動程式服務當機或無限期阻斷呼叫端。驅動程式可以傳回錯誤,或讓緩衝區內容處於未定狀態。