Synchronizacja

Mechanika planowania

Przetwarzanie danych na wykresie MediaPipe ma miejsce w węzłach przetwarzania zdefiniowanych jako podklasy CalculatorBase. To system planowania decyduje o tym, kiedy uruchomić każdy kalkulator.

Każdy wykres ma co najmniej 1 kolejkę algorytmu szeregowania. Każda kolejka algorytmu szeregowania ma dokładnie 1 executor. Węzły są statycznie przypisane do kolejki (a tym samym do wykonawcy). Domyślnie dostępna jest 1 kolejka, której wykonawcą jest pula wątków z liczbą wątków zależną od możliwości systemu.

Każdy węzeł ma stan harmonogramu, który może być niegotowy, gotowy lub uruchomiony. Funkcja gotowości określa, czy węzeł jest gotowy do uruchomienia. Ta funkcja jest wywoływana podczas inicjowania wykresu, po zakończeniu działania węzła oraz po zmianie stanu danych wejściowych węzła.

Użyta funkcja gotowości zależy od typu węzła. Węzeł bez danych wejściowych strumienia jest nazywany węzłem źródłowym. Węzły źródłowe są zawsze gotowe do działania, dopóki nie informują platformy, że nie mają już danych do wyjściowania, a wtedy są zamykane.

Węzły inne niż źródłowe są gotowe, jeśli mają dane wejściowe do przetworzenia oraz jeśli dane te stanowią prawidłowy zestaw danych wejściowych zgodny z warunkami określonymi przez zasadę wejściową węzła (opisane poniżej). Większość węzłów używa domyślnej zasady wejściowej, ale niektóre węzły określają inną.

Gdy węzeł jest gotowy, zadanie jest dodawane do odpowiedniej kolejki algorytmu szeregowania, która jest kolejką priorytetową. Funkcja priorytetu jest obecnie stała i uwzględnia statyczne właściwości węzłów oraz ich topologiczne sortowanie na wykresie. Na przykład węzły znajdujące się bliżej strony wyjściowej wykresu mają wyższy priorytet, a węzły źródłowe – najniższy.

Każda kolejka jest obsługiwana przez wykonawcę, który odpowiada za wykonanie zadania przez wywołanie kodu kalkulatora. Można udostępniać i konfigurować różnych wykonawców.Pozwala to dostosowywać wykorzystanie zasobów wykonawczych, np. uruchamiając określone węzły w wątkach o niższym priorytecie.

Synchronizacja sygnatury czasowej

Wykonywanie wykresu MediaPipe jest zdecentralizowane: nie ma zegara globalnego, a różne węzły mogą przetwarzać dane z różnych sygnatur czasowych w tym samym czasie. Pozwala to uzyskać większą przepustowość dzięki potokowi.

Jednak w wielu procesach postrzegania czasu informacje o upływie czasu są bardzo ważne. Węzły odbierające wiele strumieni wejściowych zwykle muszą w jakiś sposób koordynować. Na przykład detektor obiektów może zwrócić z ramki listę prostokątów granicznych, a te informacje mogą być przekazywane do węzła renderowania, który powinien przetworzyć je razem z pierwotną ramką.

Dlatego jednym z najważniejszych zadań platformy MediaPipe jest zapewnienie synchronizacji danych wejściowych węzłów. W kontekście mechaniki platformy podstawową rolę sygnatury czasowej odgrywają jako klucz synchronizacji.

Ponadto MediaPipe obsługuje operacje deterministyczne, co jest ważne w wielu sytuacjach (testowanie, symulacja, przetwarzanie wsadowe itp.), a jednocześnie umożliwia autorom grafów złagodzenie determinizmu tam, gdzie jest to wymagane w celu spełnienia ograniczeń w czasie rzeczywistym.

Dwa cele synchronizacji i determinizmu leżą u podstaw wielu wyborów projektowych. W szczególności pakiety przekazane do danego strumienia muszą mieć monotonicznie rosnące sygnatury czasowe – jest to przydatne nie tylko w przypadku wielu węzłów, ale także przez logikę synchronizacji. Każdy strumień ma granicę sygnatury czasowej, która jest najniższą możliwą sygnaturą czasową dozwoloną w przypadku nowego pakietu w strumieniu. Gdy nadejdzie pakiet z sygnaturą czasową T, granica automatycznie przejdzie na wartość T+1, co odpowiada wymaganiom monotonicznym. Dzięki temu platforma ma pewność, że nie otrzyma więcej pakietów z sygnaturą czasową niższą niż T.

Zasady wprowadzania

Synchronizacja jest obsługiwana lokalnie w każdym węźle z użyciem zasady wprowadzania określonej przez węzeł.

Domyślna zasada danych wejściowych, zdefiniowana przez DefaultInputStreamHandler, zapewnia determinacyjną synchronizację danych wejściowych z zapewnianiem tych gwarancji:

  • Jeśli pakiety o tej samej sygnaturze czasowej są dostarczane w wielu strumieniach wejściowych, będą one zawsze przetwarzane razem niezależnie od kolejności ich otrzymania w czasie rzeczywistym.

  • Zbiory wejściowe są przetwarzane w kolejności ściśle rosnącej według sygnatury czasowej.

  • Żadne pakiety nie są porzucane, a przetwarzanie jest w pełni deterministyczne.

  • Biorąc pod uwagę powyższe gwarancje, węzeł staje się gotowy do przetwarzania danych tak szybko, jak to możliwe.

Aby wyjaśnić, jak to działa, musimy przedstawić definicję ustalonej sygnatury czasowej. Mówimy, że sygnatura czasowa strumienia jest ustalana, jeśli jest mniejsza niż wartość graniczna sygnatury czasowej. Inaczej mówiąc, sygnatura czasowa jest ustawiana dla strumienia, gdy stan danych wejściowych w danej sygnaturze czasowej jest nieodwołalnie znany: istnieje pakiet lub istnieje pewność, że pakiet z tą sygnaturą czasową nie dotrze.

Sygnatura czasowa jest rozliczana dla wielu strumieni, jeśli jest ustalona dla każdego z nich. Poza tym jeśli sygnatura czasowa jest ustalona, oznacza to, że wszystkie wcześniejsze sygnatury czasowe również zostały przetworzone. Zatem ustalone sygnatury czasowe mogą być przetwarzane deterministycznie w kolejności rosnącej.

Biorąc pod uwagę tę definicję, kalkulator z domyślną zasadą wprowadzania jest gotowy, jeśli występuje sygnatura czasowa, która jest rozliczana we wszystkich strumieniach wejściowych i zawiera pakiet w co najmniej jednym strumieniu. Zasada wejściowa udostępnia wszystkie dostępne pakiety dla ustalonej sygnatury czasowej w postaci pojedynczego zestawu wejściowego dla kalkulatora.

Jedną z konsekwencji tego deterministycznego zachowania jest to, że w przypadku węzłów z wieloma strumieniami wejściowymi może występować teoretycznie nieograniczone oczekiwanie na wyrównanie sygnatury czasowej, a tymczasem nieograniczona liczba pakietów może być buforowana. (Weźmy węzeł z 2 strumieniami wejściowych, z których jeden wysyła pakiety, a drugi nic nie wysyła i nie przesuwa granic).

Dlatego udostępniamy też niestandardowe zasady dotyczące wprowadzania danych, na przykład dzielenie danych wejściowych na różne zbiory synchronizacji zdefiniowane przez SyncSetInputStreamHandler lub całkowite unikanie synchronizacji i przetwarzanie danych wejściowych natychmiast po ich otrzymaniu zdefiniowanego przez metodę ImmediateInputStreamHandler.

Kontrola przepływu

Występują dwa główne mechanizmy kontroli przepływu. Mechanizm wstecznego ciśnienia ogranicza wykonywanie węzłów nadrzędnych, gdy pakiety buforowane w strumieniu osiągną (konfigurowalny) limit zdefiniowany przez CalculatorGraphConfig::max_queue_size. Ten mechanizm zachowuje deterministyczny charakter i obejmuje system unikania zakleszczeń, który w razie potrzeby złagodzi skonfigurowane limity.

Drugi system obejmuje wstawianie specjalnych węzłów, które mogą odrzucać pakiety zgodnie z ograniczeniami działającymi w czasie rzeczywistym (zwykle z zastosowaniem niestandardowych zasad dotyczących wprowadzania danych) zdefiniowanymi przez FlowLimiterCalculator. Na przykład wspólny wzorzec umieszcza węzeł kontroli przepływu na danych wejściowych podgrafu, z połączeniem w pętli między danymi wyjściowymi a węzłem kontroli przepływu. Węzeł kontroli przepływu może więc śledzić liczbę sygnatur czasowych przetwarzanych na wykresie pobierania i zrzucać pakiety, gdy ta liczba osiągnie (konfigurowalny) limit. Poza tym pakiety są odrzucane, więc unikamy marnowania pracy, która wynikałaby z częściowego przetwarzania sygnatury czasowej i usuwania pakietów między etapami pośrednimi.

To podejście oparte na kalkulatorach daje autorowi wykresu kontrolę nad tym, gdzie mogą zostać porzucone pakiety, oraz pozwala na elastyczność w dostosowywaniu i dostosowywaniu działania wykresu do ograniczeń zasobów.