神經網絡HAL 1.2引入了突發執行的概念。突發執行是快速連續發生的相同準備模型的一系列執行,例如對攝像機捕獲的幀或連續音頻樣本進行的執行。突發對像用於控制一組突發執行,並在兩次執行之間保留資源,從而使執行具有較低的開銷。突發對象啟用三個優化:
- 突發對像是在執行序列之前創建的,並在序列結束時釋放。因此,猝發對象的生存期向驅動程序提示了它應該保持高性能狀態的時間。
- 突發對象可以在兩次執行之間保留資源。例如,驅動程序可以在第一次執行時映射內存對象,並將該映射緩存在突發對像中,以在後續執行中重用。當突發對像被破壞或當NNAPI運行時通知突發對像不再需要該資源時,可以釋放任何緩存的資源。
- 突發對象使用快速消息隊列(FMQ)在應用程序和驅動程序進程之間進行通信。這可以減少延遲,因為FMQ繞過了HIDL,並通過共享內存中的原子循環FIFO將數據直接傳遞到另一個進程。消費者進程知道通過輪詢FIFO中的元素數或等待生產者發出的FMQ事件標誌來使項目出隊並開始處理。此事件標誌是快速的用戶空間互斥體(futex)。
FMQ是一種低級數據結構,它不提供跨進程的生存期保證,並且沒有內置機制來確定FMQ另一端的進程是否按預期運行。因此,如果FMQ的生產者死亡,則消費者可能會被困在等待永不到達的數據。該問題的一種解決方案是,驅動程序將FMQ與更高級別的突發對象相關聯,以檢測突發執行何時結束。
由於突發執行與其他執行路徑一樣,對相同的參數進行操作並返回相同的結果,因此基礎FMQ必須將相同的數據往返於NNAPI服務驅動程序。但是,FMQ只能傳輸原始數據類型。通過直接在FMQ中對嵌套緩衝區(向量類型)進行序列化和反序列化,並使用HIDL回調對象按需傳輸內存池句柄,可以完成複雜數據的傳輸。如果隊列正在阻塞,則FMQ的生產者端必須使用MessageQueue::writeBlocking
原子地將請求或結果消息發送給使用者,如果隊列沒有阻塞,則必須使用MessageQueue::write
。
突發接口
在hardware/interfaces/neuralnetworks/1.2/
可以找到用於Neural Networks HAL的突發接口,並在下面進行描述。有關NDK層中的突發接口的更多信息,請參見frameworks/ml/nn/runtime/include/NeuralNetworks.h
。
類型.hal
types.hal
定義了通過FMQ發送的數據類型。
-
FmqRequestDatum
:執行Request
對象和MeasureTiming
值的序列化表示形式的單個元素,該元素通過快速消息隊列發送。 -
FmqResultDatum
:從執行(ErrorStatus
,OutputShapes
和Timing
)返回的值的序列化表示形式的單個元素,該值通過快速消息隊列返回。
IBurstContext.hal
IBurstContext.hal
定義了駐留在神經網絡服務中的HIDL接口對象。
-
IBurstContext
:用於管理突發資源的上下文對象。
IBurstCallback.hal
IBurstCallback.hal
為由神經網絡運行時創建的回調定義了HIDL接口對象,並且由神經網絡服務用於檢索與插槽標識符相對應的hidl_memory
對象。
- IBurstCallback :服務用於檢索內存對象的回調對象。
IPreparedModel.hal
IPreparedModel.hal
在HAL 1.2中進行了擴展,提供了一種從準備好的模型中創建IBurstContext
對象的方法。
-
configureExecutionBurst
:配置一個突發對象,該對像用於快速連續地對準備好的模型執行多個推理。
在驅動程序中支持突發執行
在HIDL NNAPI服務中支持突發對象的最簡單方法是使用突發實用程序函數::android::nn::ExecutionBurstServer::create
,該函數可在ExecutionBurstServer.h
找到,並包裝在libneuralnetworks_common
和libneuralnetworks_util
靜態庫中。此工廠函數有兩個重載:
- 一個重載接受一個指向
IPreparedModel
對象的指針。該實用程序函數在IPreparedModel
像中使用executeSynchronously
方法來執行模型。 - 一個重載接受一個可自定義的
IBurstExecutorWithCache
對象,該對象可用於緩存在多個執行過程中持續存在的資源(例如hidl_memory
映射)。
每個重載都返回一個IBurstContext
對象(代表突發對象),該對象包含並管理自己的專用偵聽器線程。該線程從requestChannel
FMQ接收請求,執行推理,然後通過resultChannel
FMQ返回結果。當突發客戶端丟失對IBurstContext
引用時,將自動釋放該線程和IBurstContext
像中包含的所有其他資源。
另外,您可以創建自己的IBurstContext
實現,該實現了解如何通過傳遞到IPreparedModel::configureExecutionBurst
的requestChannel
和resultChannel
FMQ發送和接收消息。
突發實用程序功能可在ExecutionBurstServer.h
中找到。
/**
* Create automated context to manage FMQ-based executions.
*
* This function is intended to be used by a service to automatically:
* 1) Receive data from a provided FMQ
* 2) Execute a model with the given information
* 3) Send the result to the created FMQ
*
* @param callback Callback used to retrieve memories corresponding to
* unrecognized slots.
* @param requestChannel Input FMQ channel through which the client passes the
* request to the service.
* @param resultChannel Output FMQ channel from which the client can retrieve
* the result of the execution.
* @param executorWithCache Object which maintains a local cache of the
* memory pools and executes using the cached memory pools.
* @result IBurstContext Handle to the burst context.
*/
static sp<ExecutionBurstServer> create(
const sp<IBurstCallback>& callback, const FmqRequestDescriptor& requestChannel,
const FmqResultDescriptor& resultChannel,
std::shared_ptr<IBurstExecutorWithCache> executorWithCache);
/**
* Create automated context to manage FMQ-based executions.
*
* This function is intended to be used by a service to automatically:
* 1) Receive data from a provided FMQ
* 2) Execute a model with the given information
* 3) Send the result to the created FMQ
*
* @param callback Callback used to retrieve memories corresponding to
* unrecognized slots.
* @param requestChannel Input FMQ channel through which the client passes the
* request to the service.
* @param resultChannel Output FMQ channel from which the client can retrieve
* the result of the execution.
* @param preparedModel PreparedModel that the burst object was created from.
* IPreparedModel::executeSynchronously will be used to perform the
* execution.
* @result IBurstContext Handle to the burst context.
*/
static sp<ExecutionBurstServer> create(const sp<IBurstCallback>& callback,
const FmqRequestDescriptor& requestChannel,
const FmqResultDescriptor& resultChannel,
IPreparedModel* preparedModel);
以下是在Neural Networks示例驅動程序中的frameworks/ml/nn/driver/sample/SampleDriver.cpp
找到的突發接口的參考實現。
Return<void> SamplePreparedModel::configureExecutionBurst(
const sp<V1_2::IBurstCallback>& callback,
const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
configureExecutionBurst_cb cb) {
NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
"SampleDriver::configureExecutionBurst");
// Alternatively, the burst could be configured via:
// const sp<V1_2::IBurstContext> burst =
// ExecutionBurstServer::create(callback, requestChannel,
// resultChannel, this);
//
// However, this alternative representation does not include a memory map
// caching optimization, and adds overhead.
const std::shared_ptr<BurstExecutorWithCache> executorWithCache =
std::make_shared<BurstExecutorWithCache>(mModel, mDriver, mPoolInfos);
const sp<V1_2::IBurstContext> burst = ExecutionBurstServer::create(
callback, requestChannel, resultChannel, executorWithCache);
if (burst == nullptr) {
cb(ErrorStatus::GENERAL_FAILURE, {});
} else {
cb(ErrorStatus::NONE, burst);
}
return Void();
}