本頁描述了用於在驅動程序和框架之間有效地傳遞操作數緩衝區的數據結構和方法。
在模型編譯時,框架將常量操作數的值提供給驅動程序。根據常量操作數的生存期,其值位於HIDL向量或共享內存池中。
- 如果生存期為
CONSTANT_COPY
,則這些值位於模型結構的operandValues
字段中。由於HIDL向量中的值是在進程間通信(IPC)期間複製的,因此通常僅用於保存少量數據,例如標量操作數(例如ADD
的激活標量)和小張量參數(例如,RESHAPE
的形狀張量)。 - 如果生存期為
CONSTANT_REFERENCE
,則這些值位於模型結構的pools
字段中。在IPC期間,僅共享內存池的句柄被複製,而不是複制原始值。因此,使用共享內存池比HIDL向量更有效地保存大量數據(例如卷積中的權重參數)。
在模型執行時,框架將輸入和輸出操作數的緩衝區提供給驅動程序。與可能在HIDL向量中發送的編譯時常量不同,執行的輸入和輸出數據始終通過一組內存池進行通信。
HIDL數據類型hidl_memory
在編譯和執行中均用於表示未映射的共享內存池。驅動程序應根據hidl_memory
數據類型的名稱相應地映射內存以使其可用。支持的內存名稱是:
-
ashmem
:Android共享內存。有關更多詳細信息,請參見memory 。 -
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驅動程序必須支持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
指定的所有角色的相應模型操作數以及BufferRole
提供的尺寸中BufferDesc
的尺寸。綜合所有維度信息,緩衝區可能具有未知的維度或等級。在這種情況下,緩衝器處於靈活狀態,其中當用作模型輸入時尺寸是固定的,而當用作模型輸出時處於動態狀態。相同的緩衝區可以在不同的執行中用於不同形狀的輸出,並且驅動程序必須正確處理緩衝區的大小。
內存域是一項可選功能。出於多種原因,驅動程序可以確定它不支持給定的分配請求。例如:
- 請求的緩衝區具有動態大小。
- 該驅動程序具有內存限制,使其無法處理大緩衝區。
幾個不同的線程有可能同時從驅動程序管理的緩衝區中讀取。同時訪問緩衝區以進行寫或讀/寫操作是不確定的,但是它一定不能使驅動程序服務崩潰或無限期地阻止調用者。驅動程序可以返回錯誤或使緩衝區的內容處於不確定狀態。