Esecuzioni burst e code di messaggi rapide

Neural Networks HAL 1.2 introduce il concetto di esecuzione a raffica. Serie di foto a raffica sono una sequenza di esecuzioni dello stesso modello preparato che si verificano successione rapida, come quelle che operano su fotogrammi di un'acquisizione o audio successivo i campioni. Un oggetto burst viene utilizzato per controllare una serie di esecuzioni burst e per di preservare le risorse tra un'esecuzione e l'altra, in modo che abbiano un overhead. Gli oggetti Burst consentono tre ottimizzazioni:

  1. Un oggetto burst viene creato prima di una sequenza di esecuzioni e liberato al termine della sequenza. Per questo motivo, la durata della raffica un oggetto suggerisce a un driver per quanto tempo deve rimanere in un ambiente stato.
  2. Un oggetto burst può conservare le risorse tra un'esecuzione e l'altra. Ad esempio, un Il driver può mappare un oggetto di memoria alla prima esecuzione e memorizzare nella cache il mapping nell'oggetto burst per essere riutilizzato nelle esecuzioni successive. Qualsiasi risorsa memorizzata nella cache possono essere rilasciati quando l'oggetto di burst viene eliminato o quando la NNAPI Il runtime avvisa l'oggetto di burst che la risorsa non è più necessaria.
  3. Un oggetto burst utilizza code di messaggi veloci per la comunicazione tra i processi dell'app e del conducente. Questo può ridurre la latenza perché l'FMQ ignora l'HIDL e passa i dati direttamente un altro processo attraverso un FIFO circolare atomico in memoria condivisa. La il processo del consumatore sa di dover rimuovere un articolo in coda e iniziare l'elaborazione eseguire il polling del numero di elementi nel file FIFO o attendere l'evento dell'FMQ segnalato dal produttore. Questo evento segnala una rapida mutex dello spazio utente (futex).

Un FMQ è una struttura di dati di basso livello che non offre garanzie a vita processi e non ha un meccanismo integrato per determinare se il processo dall'altra estremità di FMQ sia in esecuzione come previsto. Di conseguenza, se il produttore l'FMQ muore, il consumatore può restare bloccato in attesa di dati che non arrivano mai. Uno. a questo problema è che il conducente associno gli oggetti FMQ oggetto burst di livello superiore per rilevare quando l'esecuzione del burst è terminata.

Poiché le esecuzioni di burst operano sugli stessi argomenti e restituiscono gli stessi degli altri percorsi di esecuzione, gli oggetti FMQ sottostanti devono passare gli stessi dati e dai driver del servizio NNAPI. Tuttavia, gli FMQ possono trasferire solo i tipi di dati semplici. Il trasferimento di dati complessi si ottiene serializzando deserializzare i buffer nidificati (tipi vettoriali) direttamente negli FMQ e utilizzare Oggetti di callback HIDL per trasferire gli handle del pool di memoria on demand. Il produttore FMQ deve inviare i messaggi di richiesta o di risultato al consumer a livello atomico utilizzando MessageQueue::writeBlocking se la coda si blocca oppure utilizzando MessageQueue::write se la coda non blocca.

Interfacce di burst

Le interfacce burst per le reti neurali HAL si trovano in hardware/interfaces/neuralnetworks/1.2/ e sono descritti di seguito. Per ulteriori informazioni sulle interfacce burst nell'NDK , consulta frameworks/ml/nn/runtime/include/NeuralNetworks.h

type.hal

types.hal definisce il tipo di dati che vengono inviati attraverso FMQ.

  • FmqRequestDatum: Un singolo elemento di una rappresentazione serializzata di un'esecuzione Request e un valore MeasureTiming, che viene inviato attraverso il messaggio rapido in coda.
  • FmqResultDatum: Un singolo elemento di una rappresentazione serializzata dei valori restituiti da un'esecuzione (ErrorStatus, OutputShapes e Timing), ovvero restituiti tramite la coda dei messaggi rapidi.

IBurstContext.hal

IBurstContext.hal definisce l'oggetto dell'interfaccia HIDL che risiede nel servizio Reti neurali.

  • IBurstContext: Oggetto di contesto per gestire le risorse di un burst.

IBurstCallback.hal

IBurstCallback.hal definisce l'oggetto dell'interfaccia HIDL per un callback creato dalle reti neurali il runtime e viene utilizzato dal servizio di reti neurali per recuperare hidl_memory corrispondenti agli identificatori di slot.

  • IBurstCallback: Oggetto di callback utilizzato da un servizio per recuperare oggetti di memoria.

IPreparedModel.hal

IPreparedModel.hal è stata estesa nell'HAL 1.2 con un metodo per creare un oggetto IBurstContext da un un modello preparato.

  • configureExecutionBurst: Configura un oggetto burst utilizzato per eseguire più inferenze su un oggetto modello in rapida successione.

Sostenere le esecuzioni a raffica in un conducente

Il modo più semplice per supportare oggetti burst in un servizio HIDL NNAPI è utilizzare funzione di utilità di burst ::android::nn::ExecutionBurstServer::create, che è presenti in ExecutionBurstServer.h e confezionato in libneuralnetworks_common e libneuralnetworks_util librerie statiche. Questa funzione di fabbrica ha due sovraccarichi:

  • Un sovraccarico accetta un puntatore a un oggetto IPreparedModel. Questo una funzione di utilità utilizza il metodo executeSynchronously in una IPreparedModel per eseguire il modello.
  • Un sovraccarico accetta un oggetto IBurstExecutorWithCache personalizzabile, che può essere utilizzato per memorizzare nella cache risorse (ad esempio hidl_memory mappature) che vengono mantenuti in più esecuzioni.

Ogni sovraccarico restituisce un oggetto IBurstContext (che rappresenta il burst ) che contiene e gestisce il proprio thread listener dedicato. Questo thread riceve richieste dall'FMQ requestChannel, esegue l'inferenza, quindi restituisce i risultati tramite l'FMQ resultChannel. Questo thread e tutti gli altri Le risorse contenute nell'oggetto IBurstContext vengono rilasciate automaticamente quando il client del burst perde il riferimento a IBurstContext.

In alternativa, puoi creare la tua implementazione di IBurstContext che capisce come inviare e ricevere messaggi tramite requestChannel e resultChannel FMQ passati a IPreparedModel::configureExecutionBurst.

Le funzioni di utilità di burst si trovano 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);

Di seguito è riportata un'implementazione di riferimento di un'interfaccia di burst presente nella Driver di esempio delle reti neurali 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();
}