Neural Networks HAL 1.2 引入了「突發執行」的概念。爆發執行作業是針對同一準備好的模型,快速連續進行的一系列執行作業,例如執行相機所拍攝的影格或連續音訊取樣。爆發物件可用於控制一組爆發執行作業,並在執行作業之間保留資源,讓執行作業的額外負擔降低。Burst 物件可啟用三種最佳化功能:
- 爆發物件會在一系列執行作業之前建立,並在序列結束時釋放。因此,Burst 物件的生命週期會向驅動程式提示,該物件應以高效能狀態運作多久。
- 爆發物件可在執行作業之間保留資源。舉例來說,驅動程式可以在第一次執行時對應記憶體物件,並在爆發物件中快取對應項目,以便在後續執行作業中重複使用。當爆發物件遭到銷毀,或 NNAPI 執行階段通知爆發物件不再需要該資源時,系統可以釋出任何快取的資源。
- 爆發物件會使用快速訊息佇列 (FMQ) 在應用程式和驅動程式程序之間進行通訊。這麼做可以減少延遲時間,因為 FMQ 會略過 HIDL,並透過共用記憶體中的原子循環 FIFO 將資料直接傳遞至另一個程序。消費者程序會知道要從佇列中移除項目,並開始處理,方法是輪詢 FIFO 中的元素數量,或是等待 FMQ 的事件標記,由生產者發出信號。這個事件旗標是快速的使用者空間互斥 (futex)。
FMQ 是低階資料結構,不會在各個程序之間提供生命週期保證,也沒有內建機制可判斷 FMQ 另一端的程序是否正常執行。因此,如果 FMQ 的生產者停止運作,消費者可能會一直等待永遠不會到達的資料。解決這個問題的其中一種方法是讓驅動程式將 FMQ 與較高層級的突發物件建立關聯,以便偵測突發執行作業何時結束。
由於叢集執行作業會使用相同的引數,並傳回與其他執行路徑相同的結果,因此底層 FMQ 必須將相同資料傳遞至 NNAPI 服務驅動程式,並從中傳遞。不過,FMQ 只能傳輸舊版資料類型。傳輸複雜資料的方式,是直接在 FMQ 中序列化和反序列化巢狀緩衝區 (向量類型),並使用 HIDL 回呼物件視需求傳輸記憶體集區句柄。如果佇列為阻斷式,FMQ 的生產者端必須使用 MessageQueue::writeBlocking
將要求或結果訊息以原子方式傳送至消費者;如果佇列為非阻斷式,則應使用 MessageQueue::write
。
Burst 介面
神經網路 HAL 的突發介面位於 hardware/interfaces/neuralnetworks/1.2/
中,並在下文中加以說明。如要進一步瞭解 NDK 層中的突發介面,請參閱 frameworks/ml/nn/runtime/include/NeuralNetworks.h
。
types.hal
types.hal
定義透過 FMQ 傳送的資料類型。
FmqRequestDatum
:執行Request
物件和MeasureTiming
值的序列化表示法單一元素,會透過快速訊息佇列傳送。FmqResultDatum
:執行作業 (ErrorStatus
、OutputShapes
和Timing
) 傳回值的序列化表示法單一元素,透過快速訊息佇列傳回。
IBurstContext.hal
IBurstContext.hal
定義位於神經網路服務中的 HIDL 介面物件。
IBurstContext
:用於管理連拍相片資源的內容物件。
IBurstCallback.hal
IBurstCallback.hal
會為類神經網路執行階段建立的回呼定義 HIDL 介面物件,並由類神經網路服務用來擷取與插槽 ID 相對應的 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
物件 (代表 burst 物件),該物件會包含及管理專屬的事件監聽器執行緒。這個執行緒會接收來自 requestChannel
FMQ 的要求,執行推論,然後透過 resultChannel
FMQ 傳回結果。當 burst 的用戶端失去對 IBurstContext
的參照時,這個執行緒和 IBurstContext
物件中的所有其他資源都會自動釋出。
或者,您也可以自行建立 IBurstContext
的實作項目,瞭解如何透過傳遞至 IPreparedModel::configureExecutionBurst
的 requestChannel
和 resultChannel
FMQ 傳送及接收訊息。
您可以在 ExecutionBurstServer.h
中找到 burst 公用程式函式。
/**
* 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();
}