Wykonywanie serii i szybkie kolejki wiadomości

W wersji Neural Networks HAL 1.2 wprowadzono koncepcję wykonywania sekwencji. Burst wykonania to sekwencja wykonanych w szybkiej kolejności operacji na tym samym przygotowanym modelu, np. operacje na klatkach obrazu z kamery lub kolejnych próbkach dźwięku. Obiekt burst służy do kontrolowania zbioru operacji burst i do zachowywania zasobów między wykonaniami, dzięki czemu wykonania te mają mniejszy nakład. Obiekty burst umożliwiają 3 rodzaje optymalizacji:

  1. Obiekt burstowy jest tworzony przed sekwencją wykonań i zostaje zwolniony po zakończeniu sekwencji. Z tego względu czas trwania wybuchu wskazówki dotyczące obiektów, przekazane do sterownika, jak długo ma ono pozostawać w wysokiej wydajności stanu.
  2. Obiekt burst może zachować zasoby między wykonaniami. Na przykład plik sterownik może zmapować obiekt pamięci przy pierwszym uruchomieniu i buforować mapowanie w obiekcie burst do ponownego wykorzystania w kolejnych wykonaniach. Zasoby zapisane w pamięci podręcznej mogą zostać zwolnione, gdy obiekt burst zostanie zniszczony lub gdy środowisko uruchomieniowe NNAPI powiadomi obiekt burst, że zasób nie jest już potrzebny.
  3. Obiekt serii używa funkcji szybkie kolejki wiadomości (FMQ) do komunikacji między procesami aplikacji i sterownika. Może to zmniejszyć opóźnienie, ponieważ FMQ omija HIDL i przekazuje dane bezpośrednio do innego procesu za pomocą atomowego kołowego FIFO w wspólnej pamięci. że proces konsumenta musi usunąć element z kolejki i rozpocząć przetwarzanie ankietowanie liczby pierwiastków w FIFO lub oczekiwanie na zdarzenie FMQ flaga, co jest sygnalizowane przez producenta. Ten parametr to szybki semafor w przestrzeni użytkownika (futex).

FMQ to struktura danych niskiego poziomu, która nie gwarantuje trwałości w przypadku różnych procesów i nie ma wbudowanego mechanizmu umożliwiającego określenie, czy proces po drugiej stronie FMQ działa zgodnie z oczekiwaniami. W konsekwencji, jeśli producent FMQ przestanie działać, konsument może czekać na dane, które nigdy nie dotrą. Jeden Rozwiązaniem tego problemu jest powiązanie przez kierowcę FMQ z obiektu burst wyższego poziomu w celu wykrycia zakończenia wykonywania serii.

Ponieważ wykonania w trybie burst działają na podstawie tych samych argumentów i zwracają te same wyniki co inne ścieżki wykonania, podstawowe obiekty FMQ muszą przekazywać te same dane do i z sterowników usługi NNAPI. FMQ mogą jednak przesyłać tylko i nie tylko. Przenoszenie złożonych danych odbywa się przez serializację deserializacji zagnieżdżonych buforów (typów wektorów) bezpośrednio w FMQ; Obiekty wywołania zwrotnego HIDL do przesyłania uchwytów puli pamięci na żądanie. Producent strony FMQ muszą wysyłać do konsumenta komunikaty o żądaniach lub wynikach atomowo przy użyciu funkcji MessageQueue::writeBlocking, jeśli kolejka jest blokowana; używając metody MessageQueue::write, jeśli kolejka nie jest blokowana.

Interfejsy serii

Interfejsy burst dla HAL sieci neuronowych znajdują się w: hardware/interfaces/neuralnetworks/1.2/ i zostały opisane poniżej. Więcej informacji o interfejsach burst w warstwie NDK znajdziesz w artykule frameworks/ml/nn/runtime/include/NeuralNetworks.h (w języku angielskim).

types.hal

types.halokreśla typ danych przesyłanych przez FMQ.

  • FmqRequestDatum: Pojedynczy element serializowanej reprezentacji wykonania Request i wartość MeasureTiming, która jest wysyłana przez szybką wiadomość kolejkę.
  • FmqResultDatum: Pojedynczy element zserializowanej reprezentacji wartości zwracanych z wykonania (ErrorStatus, OutputShapes i Timing), które jest w ramach szybkiej kolejki komunikatów.

IBurstContext.hal

IBurstContext.hal określa obiekt interfejsu HIDL, który funkcjonuje w usłudze sieci neuronowych.

  • IBurstContext: Obiekt kontekstu do zarządzania zasobami serii.

IBurstCallback.hal

IBurstCallback.hal określa interfejs HIDL dla wywołania zwrotnego utworzonego przez środowisko wykonawcze Neural Networks i jest używany przez usługę Neural Networks do pobierania obiektów hidl_memory odpowiadających identyfikatorom slotów.

  • IBurstCallback: obiekt wywołania zwrotnego używany przez usługę do pobierania obiektów pamięci.

IPreparedModel.hal

IPreparedModel.hal w HAL 1.2 został rozszerzony o metodę umożliwiającą tworzenie obiektu IBurstContext na podstawie przygotowanego modelu.

  • configureExecutionBurst: Konfiguruje obiekt burst używany do wykonywania wielu wnioskowania na przygotowanej bazie w szybkim modelowaniu statystycznym.

Obsługa wykonań burst w sterowniku

Najprostszym sposobem obsługi obiektów burst w usłudze HIDL NNAPI jest użycie burst funkcji użytkowej ::android::nn::ExecutionBurstServer::create, która jest znaleziono w ExecutionBurstServer.h i spakowane w libneuralnetworks_common i libneuralnetworks_util bibliotek statycznych. Ta funkcja fabryczna ma 2 przeciążenia:

  • Jedna z przeciążeń przyjmuje wskaźnik do obiektu IPreparedModel. Ta funkcja pomocnicza używa metody executeSynchronously w obiekcie IPreparedModel do wykonania modelu.
  • Jedno przeciążenie akceptuje konfigurowalny obiekt IBurstExecutorWithCache, która może być używana do buforowania zasobów (np. mapowań hidl_memory), które pozostają aktywne w wielu uruchomieniach.

Każde przeciążenie zwraca obiekt IBurstContext (który reprezentuje serię obiekt), który zawiera własny wątek detektora dedykowanego detektora i nim zarządza. Ten wątek odbiera żądania z poziomu kolejki FMQ requestChannel, wykonuje wnioskowanie, a następnie zwraca wyniki przez kolejkę FMQ resultChannel. Ten wątek i wszystkie inne zasoby zawarte w obiekcie IBurstContext są automatycznie uwalniane, gdy klient bursta traci odwołanie do IBurstContext.

Możesz też utworzyć własną implementację tagu IBurstContext, która wie, jak wysyłać i odbierać wiadomości przez requestChannel oraz Do IPreparedModel::configureExecutionBurst przekazano resultChannel FMQ.

Funkcje burstowe znajdują się w 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);

Poniżej znajduje się referencyjna implementacja interfejsu burst, która znajduje się w przykładowym sterowniku Neural Networks w pliku 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();
}