Wykonania seryjne i szybkie kolejki wiadomości

Sieci neuronowe HAL 1.2 wprowadza koncepcję wykonywania serii. Wykonanie serii to sekwencja wykonań tego samego przygotowanego modelu, które następują szybko po sobie, na przykład te operujące na klatkach przechwytywania kamery lub kolejnych próbek audio. Obiekt serii służy do kontrolowania zbioru wykonań serii i do zachowania zasobów między wykonaniami, dzięki czemu wykonania mają mniejszy narzut. Obiekty typu Burst umożliwiają trzy optymalizacje:

  1. Obiekt burst jest tworzony przed sekwencją egzekucji i zwalniany po zakończeniu sekwencji. Z tego powodu czas życia obiektu burst wskazuje kierowcy, jak długo powinien pozostawać w stanie wysokiej wydajności.
  2. Obiekt burst może zachować zasoby między wykonaniami. Na przykład sterownik może mapować obiekt pamięci przy pierwszym wykonaniu i buforować mapowanie w obiekcie burst w celu ponownego wykorzystania w kolejnych wykonaniach. Każdy zasób z pamięci podręcznej można zwolnić, gdy obiekt rozdziału zostanie zniszczony lub gdy środowisko wykonawcze NNAPI powiadomi obiekt rozdziału, że zasób nie jest już potrzebny.
  3. Obiekt burst używa szybkich kolejek komunikatów (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 przez atomowy cykliczny FIFO w pamięci współdzielonej. Proces konsumenta wie, że należy usunąć element z kolejki i rozpocząć przetwarzanie, odpytując liczbę elementów w FIFO lub czekając na flagę zdarzenia FMQ, która jest sygnalizowana przez producenta. Ta flaga zdarzenia to szybki mutex w przestrzeni użytkownika (futex).

FMQ to struktura danych niskiego poziomu, która nie oferuje żadnych gwarancji dożywotnich między procesami i nie ma wbudowanego mechanizmu określania, czy proces na drugim końcu FMQ działa zgodnie z oczekiwaniami. W konsekwencji, jeśli umrze producent FMQ, konsument może utknąć w oczekiwaniu na dane, które nigdy nie nadejdą. Jednym z rozwiązań tego problemu jest skojarzenie przez sterownik FMQ z obiektem burst wyższego poziomu w celu wykrycia, kiedy wykonywanie zdjęć seryjnych zostało zakończone.

Ponieważ wykonania serii działają na tych samych argumentach i zwracają te same wyniki, co inne ścieżki wykonywania, bazowe FMQ muszą przekazywać te same dane do i ze sterowników usługi NNAPI. Jednak FMQ mogą przesyłać tylko zwykłe, stare typy danych. Przesyłanie złożonych danych jest realizowane przez serializację i deserializację zagnieżdżonych buforów (typów wektorowych) bezpośrednio w FMQ oraz użycie obiektów wywołania zwrotnego HIDL do przesyłania na żądanie obsługi puli pamięci. Strona producenta FMQ musi wysyłać żądania lub komunikaty wynikowe do konsumenta niepodzielnie przy użyciu MessageQueue::writeBlocking , jeśli kolejka jest blokowana, lub przy użyciu MessageQueue::write ::write, jeśli kolejka nie blokuje.

Burst interfejsy

Interfejsy burst dla warstwy HAL sieci neuronowych znajdują się w hardware/interfaces/neuralnetworks/1.2/ i są opisane poniżej. Aby uzyskać więcej informacji na temat interfejsów burst w warstwie NDK, zobacz frameworks/ml/nn/runtime/include/NeuralNetworks.h .

typy.hal

types.hal definiuje typ danych przesyłanych przez FMQ.

  • FmqRequestDatum : Pojedynczy element serializowanej reprezentacji obiektu Request wykonania i wartości MeasureTiming , który jest wysyłany przez szybką kolejkę komunikatów.
  • FmqResultDatum : pojedynczy element serializowanej reprezentacji wartości zwracanych z wykonania ( ErrorStatus , OutputShapes i Timing ), który jest zwracany przez szybką kolejkę komunikatów.

IBurstContext.hal

IBurstContext.hal definiuje obiekt interfejsu HIDL, który znajduje się w usłudze sieci neuronowych.

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

IBurstCallback.hal

IBurstCallback.hal definiuje obiekt interfejsu HIDL dla wywołania zwrotnego utworzonego przez środowisko uruchomieniowe Neural Networks i jest używany przez usługę Neural Networks do pobierania obiektów hidl_memory odpowiadających identyfikatorom gniazd.

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

IPreparedModel.hal

IPreparedModel.hal jest rozszerzony w HAL 1.2 o metodę tworzenia obiektu IBurstContext z przygotowanego modelu.

  • configureExecutionBurst : Konfiguruje obiekt burst używany do szybkiego wykonywania wielu wniosków na przygotowanym modelu.

Obsługa wykonywania serii w sterowniku

Najprostszym sposobem obsługi obiektów burst w usłudze NNAPI HIDL jest użycie funkcji narzędzia burst ::android::nn::ExecutionBurstServer::create , która znajduje się w ExecutionBurstServer.h jest spakowana w bibliotekach statycznych libneuralnetworks_common i libneuralnetworks_util . Ta funkcja fabryczna ma dwa przeciążenia:

  • Jedno Przeciążenie akceptuje wskaźnik do obiektu IPreparedModel . Ta funkcja narzędzia używa metody executeSynchronously w obiekcie IPreparedModel do wykonania modelu.
  • Jedno Przeciążenie akceptuje konfigurowalny obiekt IBurstExecutorWithCache , który może służyć do buforowania zasobów (takich jak mapowania hidl_memory ), które są zachowywane w wielu wykonaniach.

Każde Przeciążenie zwraca obiekt IBurstContext (który reprezentuje obiekt serii), który zawiera własny dedykowany wątek odbiornika i zarządza nim. Ten wątek odbiera żądania z requestChannel FMQ, wykonuje wnioskowanie, a następnie zwraca wyniki za pośrednictwem resultChannel FMQ. Ten wątek i wszystkie inne zasoby zawarte w obiekcie IBurstContext są automatycznie zwalniane, gdy klient serii straci odwołanie do IBurstContext .

Alternatywnie możesz utworzyć własną implementację IBurstContext , która rozumie, jak wysyłać i odbierać komunikaty za pośrednictwem requestChannel i resultChannel FMQs przekazanych do IPreparedModel::configureExecutionBurst .

Funkcje narzędzia burst można znaleźć 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 można znaleźć w przykładowym sterowniku sieci neuronowych pod adresem 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();
}