Burst-Ausführungen und schnelle Nachrichtenwarteschlangen

Mit neuronalen Netzwerken HAL 1.2 wird das Konzept der Burst-Ausführungen eingeführt. Bilderfolge Ausführungen sind Abfolge von Ausführungen desselben vorbereiteten Modells, die in eine schnelle Abfolge, z. B. bei Kameraaufnahmen oder aufeinanderfolgende Audioinhalte Proben. Ein Burst-Objekt wird verwendet, um eine Reihe von Burst-Ausführungen zu steuern Ressourcen zwischen den Ausführungen aufrechtzuerhalten, sodass Ausführungen koordiniert. Burst-Objekte ermöglichen drei Optimierungen:

  1. Ein Burst-Objekt wird vor einer Reihe von Ausführungen erstellt und freigegeben. wenn die Sequenz beendet ist. Aus diesem Grund kann die Lebensdauer des Bursts Ein Objekt gibt einem Fahrer an, wie lange er in einer Hochleistungsklasse bleiben soll. Bundesstaat.
  2. Ein Burst-Objekt kann Ressourcen zwischen Ausführungen beibehalten. Beispiel: Treiber kann bei der ersten Ausführung ein Speicherobjekt zuordnen und die Zuordnung im Cache speichern. im Burst-Objekt zur Wiederverwendung in nachfolgenden Ausführungen. Beliebige im Cache gespeicherte Ressource kann freigegeben werden, wenn das Burst-Objekt zerstört oder die NNAPI Laufzeit benachrichtigt das Burst-Objekt, dass die Ressource nicht mehr benötigt wird.
  3. Ein Burst-Objekt verwendet schnelle Nachrichtenwarteschlangen (FMQs) für die Kommunikation zwischen Anwendungs- und Treiberprozessen. Dies kann Latenz zu reduzieren, da FMQ HIDL umgeht und Daten direkt an durch einen atomaren kreisförmigen FIFO im gemeinsamen Speicher weiter. Die Verbraucherprozess weiß, dass ein Artikel aus der Warteschlange entfernt und die Verarbeitung entweder durch Abfragen der Anzahl von Elementen im FIFO oder Warten auf das FMQ-Ereignis , das vom Ersteller signalisiert wird. Diese Ereigniskennzeichnung gibt eine schnelle Userspace-Mutex (Futex).

Ein FMQ ist eine Low-Level-Datenstruktur, die keine lebenslangen Garantien für und verfügt über keinen integrierten Mechanismus, um festzustellen, ob der Prozess anderen Ende des FMQ wie erwartet ausgeführt wird. Wenn also der Produzent wenn der FMQ abbricht, könnte der Nutzer auf Daten warten, die nie ankommen. Eins Die Lösung für dieses Problem besteht darin, dass der Treiber FMQs höherem Burst-Objekt, um zu erkennen, wann die Burst-Ausführung beendet ist.

Da Burst-Ausführungen mit denselben Argumenten arbeiten und dieselben wie andere Ausführungspfade ergeben, müssen die zugrunde liegenden FMQs dieselben Daten an und von den NNAPI-Diensttreibern. FMQs können jedoch nur altbekannte Datentypen. Die Übertragung komplexer Daten erfolgt durch Serialisierung und Deserialisieren verschachtelter Puffer (Vektortypen) direkt in den FMQs und verwenden HIDL-Callback-Objekte zum Übertragen von Arbeitsspeicherpool-Handles bei Bedarf. Der Produzent Seite der FMQ muss die Anfrage oder die Ergebnisnachrichten an den Nutzer senden. untrennbar mit MessageQueue::writeBlocking, wenn die Warteschlange blockiert wird, oder indem Sie MessageQueue::write verwenden, wenn die Warteschlange nicht blockierend ist.

Burst-Benutzeroberflächen

Die Burst-Schnittstellen für die HAL von neuronalen Netzwerken befinden sich in hardware/interfaces/neuralnetworks/1.2/ und werden im Folgenden beschrieben. Weitere Informationen zu Burst-Schnittstellen im NDK finden Sie unter frameworks/ml/nn/runtime/include/NeuralNetworks.h

Typen.hal

types.hal definiert den Datentyp, der über den FMQ gesendet wird.

  • FmqRequestDatum: Ein einzelnes Element einer serialisierten Darstellung einer Ausführung Request und ein MeasureTiming-Wert, der über die schnelle Nachricht gesendet wird. in die Warteschlange stellen.
  • FmqResultDatum: Ein einzelnes Element einer serialisierten Darstellung der von eine Ausführung (ErrorStatus, OutputShapes und Timing), die die in der Warteschlange für schnelle Nachrichten zurückgegeben werden.

iBurstContext.hal

IBurstContext.hal definiert das HIDL-Schnittstellenobjekt im Dienst für neuronale Netzwerke.

  • IBurstContext: Kontextobjekt zum Verwalten der Ressourcen eines Bursts.

iBurstCallback.hal

IBurstCallback.hal definiert das HIDL-Schnittstellenobjekt für einen Callback, der von neuronalen Netzwerken erstellt wurde Laufzeit und wird vom Dienst für neuronale Netzwerke verwendet, um hidl_memory abzurufen -Objekten, die Slot-IDs entsprechen.

  • IBurstCallback: Callback-Objekt, das von einem Dienst zum Abrufen von Speicherobjekten verwendet wird.

iPreparedModel.hal

IPreparedModel.hal wird in HAL 1.2 um eine Methode zum Erstellen eines IBurstContext-Objekts aus einem vorbereiteten Modell verwenden.

  • configureExecutionBurst: Konfiguriert ein Burst-Objekt, das zum Ausführen mehrerer Inferenzen für ein vorbereitetes Objekt verwendet wird aufeinanderfolgen.

Burst-Ausführungen in einem Treiber unterstützen

Die einfachste Möglichkeit zur Unterstützung von Burst-Objekten in einem HIDL NNAPI-Dienst ist die Verwendung des Burst-Dienstprogrammfunktion ::android::nn::ExecutionBurstServer::create, die gefunden in ExecutionBurstServer.h und verpackt in libneuralnetworks_common und libneuralnetworks_util statische Bibliotheken. Diese Factory-Funktion hat zwei Überlastungen:

  • Für die Überlastung kann ein Zeiger auf ein IPreparedModel-Objekt verwendet werden. Dieses die Methode executeSynchronously in einer IPreparedModel-Objekt, um das Modell auszuführen.
  • Für eine Überlast wird ein anpassbares IBurstExecutorWithCache-Objekt akzeptiert. mit dem Ressourcen im Cache gespeichert werden können (z. B. hidl_memory-Zuordnungen), die und bleiben über mehrere Ausführungen hinweg erhalten.

Bei jeder Überlastung wird ein IBurstContext-Objekt zurückgegeben, das den Burst darstellt. -Objekt), das seinen eigenen dedizierten Listener-Thread enthält und verwaltet. Dieser Thread empfängt Anfragen vom requestChannel-FMQ, führt die Inferenz durch und gibt die Ergebnisse über den FMQ resultChannel zurück. Dieser Thread und alle anderen Im IBurstContext-Objekt enthaltene Ressourcen werden automatisch freigegeben wenn der Client der Bursts seinen Verweis auf IBurstContext verliert.

Alternativ können Sie Ihre eigene Implementierung von IBurstContext erstellen, die das Senden und Empfangen von Nachrichten über requestChannel und resultChannel FMQs wurden an IPreparedModel::configureExecutionBurst übergeben.

Die Burst-Dienstprogrammfunktionen finden Sie in 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);

Im Folgenden finden Sie eine Referenzimplementierung einer Burst-Schnittstelle, die sich in der Beispieltreiber für neuronale Netzwerke unter 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();
}