Google is committed to advancing racial equity for Black communities. See how.
本頁面由 Cloud Translation API 翻譯而成。
Switch to English

突發執行和快速消息隊列

神經網絡HAL 1.2引入了突發執行的概念。突發執行是快速連續發生的相同準備模型的一系列執行,例如對攝像機捕獲的幀或連續音頻樣本進行的執行。突發對像用於控制一組突發執行,並在兩次執行之間保留資源,從而使執行具有較低的開銷。突發對象啟用三個優化:

  1. 突發對像是在執行序列之前創建的,並在序列結束時釋放。因此,猝發對象的生存期向驅動程序提示了它應該保持高性能狀態的時間。
  2. 突發對象可以在兩次執行之間保留資源。例如,驅動程序可以在第一次執行時映射內存對象,並將該映射緩存在突發對像中,以在後續執行中重用。當突發對像被破壞或當NNAPI運行時通知突發對像不再需要該資源時,可以釋放任何緩存的資源。
  3. 突發對象使用快速消息隊列(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 :從執行( ErrorStatusOutputShapesTiming )返回的值的序列化表示形式的單個元素,該值通過快速消息隊列返回。

IBurstContext.hal

IBurstContext.hal定義了駐留在神經網絡服務中的HIDL接口對象。

IBurstCallback.hal

IBurstCallback.hal為由神經網絡運行時創建的回調定義了HIDL接口對象,並且由神經網絡服務用於檢索與插槽標識符相對應的hidl_memory對象。

IPreparedModel.hal

IPreparedModel.hal在HAL 1.2中進行了擴展,提供了一種從準備好的模型中創建IBurstContext對象的方法。

  • configureExecutionBurst :配置一個突發對象,該對像用於快速連續地對準備好的模型執行多個推理。

在驅動程序中支持突發執行

在HIDL NNAPI服務中支持突發對象的最簡單方法是使用突發實用程序函數::android::nn::ExecutionBurstServer::create ,該函數可在ExecutionBurstServer.h找到,並包裝在libneuralnetworks_commonlibneuralnetworks_util靜態庫中。此工廠函數有兩個重載:

  • 一個重載接受一個指向IPreparedModel對象的指針。該實用程序函數在IPreparedModel像中使用executeSynchronously方法來執行模型。
  • 一個重載接受一個可自定義的IBurstExecutorWithCache對象,該對象可用於緩存在多個執行過程中持續存在的資源(例如hidl_memory映射)。

每個重載都返回一個IBurstContext對象(代表突發對象),該對象包含並管理自己的專用偵聽器線程。該線程從requestChannel FMQ接收請求,執行推理,然後通過resultChannel FMQ返回結果。當突發客戶端丟失對IBurstContext引用時,將自動釋放該線程和IBurstContext像中包含的所有其他資源。

另外,您可以創建自己的IBurstContext實現,該實現了解如何通過傳遞到IPreparedModel::configureExecutionBurstrequestChannelresultChannel 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();
}