神經網路 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
如果隊列是非阻塞的)。
突發介面
神經網路 HAL 的突發介面可在hardware/interfaces/neuralnetworks/1.2/
中找到,如下所述。有關 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);
以下是在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();
}