Neural Networks HAL 1.2 zawiera koncepcję wykonań burstowych. Uruchomienia serii to sekwencja wykonań tego samego gotowego modelu w krótkich odstępach czasu, np. w ramach ujęcia z kamery lub kilku kolejnych próbek audio. Obiekt burst służy do sterowania zestawem wykonań burst i do przechowywania zasobów między wykonaniami, dzięki czemu wykonania mają mniejsze wymagania. W przypadku obiektów serii można włączyć 3 optymalizacje:
- Obiekt burstowy jest tworzony przed sekwencją wykonań i uwalniany po jej zakończeniu. Z tego powodu czas życia obiektu serii wskazuje sterownikowi, jak długo ma on pozostawać w stanie wysokiej wydajności.
- Obiekt burstowy może zachowywać zasoby między uruchomieniami. Na przykład sterownik może zmapować obiekt pamięci przy pierwszym wykonaniu i buforować mapowanie w obiekcie burst do ponownego wykorzystania w kolejnych wykonaniach. Każdy zasób w pamięci podręcznej może zostać zwolniony po zniszczeniu obiektu burst lub gdy środowisko wykonawcze NNAPI powiadomi obiekt burst, że zasób nie jest już wymagany.
- Obiekt serii używa szybkich kolejek wiadomości (FMQ) do komunikacji między procesami aplikacji i sterowników. Może to zmniejszyć opóźnienie, ponieważ FMQ omija HIDL i przekazuje dane bezpośrednio do innego procesu przez niepodzielny okrągły obiekt FIFO w pamięci współdzielonej. Proces konsumenta wie, że powinien usunąć produkt z kolejki i rozpocząć przetwarzanie przez sprawdzenie liczby elementów w FIFO lub oczekiwanie na flagę zdarzenia FMQ sygnalizowaną przez producenta. To szybkie mutex (futex) w przestrzeni użytkownika.
FMQ to niskopoziomowa struktura danych, która nie daje żadnych gwarancji bezterminowych gwarancji dotyczących różnych procesów i nie ma wbudowanego mechanizmu, który określałby, czy proces po drugiej stronie FMQ działa zgodnie z oczekiwaniami. W związku z tym, jeśli producent FMQ umrze, konsument może utknąć w oczekiwaniu na dane, które nigdy nie zostaną dostarczone. Jednym z rozwiązań tego problemu jest powiązanie przez sterownika FMQ z obiektem burst wyższego poziomu w celu wykrycia zakończenia wykonywania serii.
Ponieważ wykonania burstowe działają na tych samych argumentach i zwracają te same wyniki co inne ścieżki wykonania, bazowe moduły FMQ muszą przekazywać te same dane do i od sterowników usługi NNAPI. FMQ mogą jednak przesyłać
tylko stare typy danych. Przenoszenie złożonych danych odbywa się przez serializację i deserializację zagnieżdżonych buforów (typów wektorów) bezpośrednio w FMQ, a także przez użycie obiektów wywołań zwrotnych HIDL do przesyłania uchwytów puli pamięci na żądanie. FMQ po stronie producenta musi wysyłać do konsumenta komunikaty żądania lub wyników osobno, używając MessageQueue::writeBlocking
, jeśli kolejka jest blokowana, lub MessageQueue::write
, jeśli kolejka nie jest blokowana.
Interfejsy serii
Interfejsy burst dla interfejsu HAL sieci neuronowych znajdziesz w sekcji hardware/interfaces/neuralnetworks/1.2/
i opisane poniżej. Więcej informacji o interfejsach burst w warstwie NDK znajdziesz w frameworks/ml/nn/runtime/include/NeuralNetworks.h
.
type.hal
types.hal
określa typ danych, które są wysyłane przez FMQ.
FmqRequestDatum
: pojedynczy element zserializowanej reprezentacji obiektuRequest
wykonania i wartościMeasureTiming
, która jest wysyłana w kolejce szybkich wiadomości.FmqResultDatum
: pojedynczy element zserializowanej reprezentacji wartości zwracanych przez wykonanie (ErrorStatus
,OutputShapes
iTiming
), które są zwracane przez szybką kolejkę wiadomości.
IBurstContext.hal
IBurstContext.hal
definiuje obiekt interfejsu HIDL, który funkcjonuje 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 wykonawcze sieci neuronowych i jest używany przez usługę sieci neuronowych do pobierania obiektów hidl_memory
odpowiadających identyfikatorom przedziałów.
- IBurstCallback: obiekt wywołania zwrotnego używany przez usługę do pobierania obiektów pamięci.
IPreparedModel.hal
Metoda IPreparedModel.hal
jest rozszerzona w HAL 1.2 o metodę tworzenia obiektu IBurstContext
z gotowego modelu.
configureExecutionBurst
: konfiguruje obiekt burst używany do wykonywania w krótkim czasie wielu wnioskowania na gotowym modelu.
Obsługa wykonań burst w sterowniku
Najprostszym sposobem obsługi obiektów burst w usłudze HIDL NNAPI jest użycie funkcji burstowej ::android::nn::ExecutionBurstServer::create
, którą można znaleźć w ExecutionBurstServer.h
i spakować do bibliotek statycznych libneuralnetworks_common
i libneuralnetworks_util
. Ta funkcja fabryczna ma 2 przeciążenia:
- Jedno przeciążenie akceptuje wskaźnik do obiektu
IPreparedModel
. Ta funkcja narzędziowa korzysta z metodyexecuteSynchronously
w obiekcieIPreparedModel
do wykonywania modelu. - W ramach jednego przeciążenia możliwy jest konfigurowalny obiekt
IBurstExecutorWithCache
, który może być używany do buforowania zasobów (np. mapowańhidl_memory
), które są przechowywane w wielu wykonaniach.
Każde przeciążenie zwraca obiekt IBurstContext
(reprezentujący obiekt Burst), który zawiera własny wątek detektora i nim zarządza. Ten wątek odbiera żądania od FMQ requestChannel
, przeprowadza wnioskowanie, a następnie zwraca wyniki przez FMQ resultChannel
. Ten wątek i wszystkie pozostałe zasoby zawarte w obiekcie IBurstContext
są automatycznie zwalniane, gdy klient serii utraci odniesienie do IBurstContext
.
Możesz też utworzyć własną implementację IBurstContext
, która rozumie, jak wysyłać i odbierać wiadomości przez requestChannel
i resultChannel
FMQ przekazywane 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 bursta w przykładowym sterowniku sieci neuronowych w 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();
}