Wykonywanie zadań w krótkich odstępach czasu i szybkie kolejki wiadomości

Neural Networks HAL 1.2 zawiera koncepcję wykonań burstowych. 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 powodu czas trwania obiektu burst wskazuje sterownikowi, jak długo powinien on pozostawać w stanie wysokiej wydajności.
  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. Dowolny zasób z pamięci podręcznej może zostać zwolniona, gdy obiekt burst zostanie zniszczony lub gdy interfejs NNAPI środowisko wykonawcze powiadamia obiekt Burst, że zasób nie jest już wymagany.
  3. Obiekt burst używa kolejek szybkich wiadomości (FMQ) do komunikacji między procesami aplikacji a sterownika. Może to spowodować zmniejsza opóźnienie, ponieważ FMQ omija HIDL i przekazuje dane bezpośrednio przez atomowy okrągły proces FIFO we wspólnej pamięci. Proces konsumenta wie, że ma pobrać element z kolejki i rozpocząć przetwarzanie, albo przez sprawdzenie liczby elementów w kolejce FIFO, albo przez oczekiwanie na flagę zdarzenia kolejki FMQ, która jest sygnalizowana przez producenta. Ta flaga zdarzenia jest szybka mutex w przestrzeni użytkownika (futex).

FMQ to niskopoziomowa struktura danych, która nie daje żadnych bezterminowych gwarancji procesów i nie ma wbudowanego mechanizmu określającego, czy dany proces drugi koniec 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ą. Jednym z rozwiązań tego problemu jest powiązanie obiektów FMQ z obiektem burst wyższego poziomu, aby wykryć, kiedy zakończyło się wykonywanie burstu.

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 w ramach kolejki FMQ musi wysłać żądanie lub wiadomości z wynikiem do konsumenta w sposób atomowy, używając funkcji MessageQueue::writeBlocking, jeśli kolejka jest blokująca, lub funkcji MessageQueue::write, jeśli kolejka nie jest blokująca.

Interfejsy burst

Interfejsy burst dla HAL-a sieci neuronowych znajdują się w pliku hardware/interfaces/neuralnetworks/1.2/ i są opisane poniżej. Więcej informacji o interfejsach burst w pakiecie NDK patrz frameworks/ml/nn/runtime/include/NeuralNetworks.h

type.hal

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

  • FmqRequestDatum: pojedynczy element serializowanej reprezentacji obiektu Request wykonania i wartość MeasureTiming, które są wysyłane przez kolejkę szybkich wiadomości.
  • 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 znajduje się w usłudze Neural Networks.

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

IBurstCallback.hal

IBurstCallback.hal określa obiekt interfejsu HIDL dla wywołania zwrotnego utworzonego przez sieci neuronowe w czasie działania i jest używany przez usługę sieci neuronowych do pobierania danych hidl_memory odpowiadających identyfikatorami przedziałó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 funkcji narzędzia burst ::android::nn::ExecutionBurstServer::create, która znajduje się w ExecutionBurstServer.h i jest pakowana w bibliotekach statycznych libneuralnetworks_commonlibneuralnetworks_util. Ta funkcja fabryczna ma 2 przeciążenia:

  • Jedno przeciążenie akceptuje wskaźnik do obiektu IPreparedModel. Ten funkcja użytkowa korzysta z metody executeSynchronously w funkcji IPreparedModel obiekt do wykonania modelu.
  • Jedna z przeciążeń przyjmuje obiekt IBurstExecutorWithCache, który można wykorzystać do przechowywania w pamięci podręcznej zasobów (np. mapowań hidl_memory), które są trwałe w przypadku wielu wykonań.

Każda z przeciążeń zwraca obiekt IBurstContext (który reprezentuje obiekt burst), który zawiera i zarządza własnym wątkiem odsłuchu. Ten wątek odbiera żądania z poziomu kolejki FMQ requestChannel, wykonuje wnioskowanie, a następnie zwraca wyniki przez kolejkę FMQ resultChannel. Ten i wszystkie inne zasoby zawarte w obiekcie IBurstContext są automatycznie zwalniane gdy klient serii utraci odwołanie do IBurstContext.

Możesz też utworzyć własną implementację interfejsu IBurstContext, który potrafi wysyłać i odbierać wiadomości za pomocą interfejsów FMQ requestChannelresultChannel przekazywanych do IPreparedModel::configureExecutionBurst.

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óry znajduje się w Przykładowy sterownik sieci neuronowych ma 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();
}