W wersji Neural Networks HAL 1.2 wprowadzono koncepcję wykonywania w setek. 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 wykonań burst i do zachowywania zasobów między wykonaniami, dzięki czemu wykonania te mają mniejszy nakład. Obiekty burst umożliwiają 3 optymalizacje:
- Obiekt burst jest tworzony przed sekwencją wykonywania, a zwalniany po jej zakończeniu. Z tego powodu czas trwania obiektu burst sugeruje kierowcy, jak długo powinien on pozostawać w stanie wysokiej wydajności.
- Obiekt burst może zachować zasoby między wykonaniami. Na przykład sterownik może mapować obiekt pamięci podczas pierwszego wykonania i przechowywać w pamięci podręcznej mapowanie w obiekcie burst, aby można było go ponownie użyć w kolejnych wykonaniach. Każdy zasób w pamięci podręcznej może zostać zwolniony, gdy obiekt burst zostanie zniszczony lub gdy środowisko uruchomieniowe NNAPI powiadomi obiekt burst, że zasób nie jest już potrzebny.
- Obiekt burst używa kolejek szybkich wiadomości (FMQ) do komunikacji między procesami aplikacji a 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. Proces konsumenta wie, że ma pobrać element z kolejki i rozpocząć przetwarzanie, albo przez odczytywanie liczby elementów w FIFO, albo przez oczekiwanie na flagę zdarzenia FMQ, która jest sygnalizowana 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ą do odbiorcy. Jednym z rozwiązań tego problemu jest powiązanie kolejek 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ług NNAPI. Jednak w przypadku interfejsów FMQ można przesyłać tylko typy danych plain-old. Przesyłanie złożonych danych odbywa się przez serializację i deserializację zagnieżdżonych buforów (typów wektorów) bezpośrednio w obiektach FMQ oraz przez używanie obiektów wywołania obsługi zdarzeń HIDL do przenoszenia 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 serii
Interfejsy burst dla HAL-a sieci neuronowych znajdują się w hardware/interfaces/neuralnetworks/1.2/
i są opisane poniżej. Więcej informacji o interfejsach burst w warstwie NDK znajdziesz w artykule frameworks/ml/nn/runtime/include/NeuralNetworks.h
.
types.hal
types.hal
określa typ danych przesyłanych przez FMQ.
FmqRequestDatum
: pojedynczy element serializowanej reprezentacji obiektuRequest
wykonania i wartośćMeasureTiming
, które są wysyłane za pomocą kolejki szybkich wiadomości.FmqResultDatum
: pojedynczy element serializowanej reprezentacji wartości zwracanych przez wykonanie (ErrorStatus
,OutputShapes
iTiming
), zwracany przez kolejkę szybkich wiadomości.
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 burstu.
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ą utworzenie obiektu IBurstContext
na podstawie przygotowanego modelu.
configureExecutionBurst
: Konfiguruje obiekt burst używany do wykonywania wielu wnioskowań na przygotowanym modelu w szybkiej kolejności.
Obsługa wykonywania 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_common
i libneuralnetworks_util
. Ta funkcja fabryczna ma 2 przeciążenia:
- Jedna z przeciążeń przyjmuje wskaźnik do obiektu
IPreparedModel
. Ta funkcja pomocnicza używa metodyexecuteSynchronously
w obiekcieIPreparedModel
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 wielu wykonaniach.
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 requestChannel
FMQ, wykonuje wnioskowanie, a następnie zwraca wyniki przez resultChannel
FMQ. 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ę interfejsu IBurstContext
, która potrafi wysyłać i odbierać wiadomości za pomocą interfejsów FMQ requestChannel
i resultChannel
przekazywanych do IPreparedModel::configureExecutionBurst
.
Funkcje narzędzia burst znajdziesz 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();
}