Testowanie wydajności

Android 8.0 zawiera testy wydajności spoiwa i hwbindera pod kątem przepustowości i opóźnień. Chociaż istnieje wiele scenariuszy wykrywania zauważalnych problemów z wydajnością, uruchamianie takich scenariuszy może być czasochłonne, a wyniki często są niedostępne do czasu integracji systemu. Korzystanie z dostarczonych testów wydajności ułatwia testowanie podczas programowania, wykrywa poważne problemy wcześniej i poprawia komfort użytkownika.

Testy wydajności obejmują następujące cztery kategorie:

  • przepustowość segregatora (dostępna w system/libhwbinder/vts/performance/Benchmark_binder.cpp )
  • opóźnienie segregatora (dostępne w frameworks/native/libs/binder/tests/schd-dbg.cpp )
  • przepustowość hwbindera (dostępna w system/libhwbinder/vts/performance/Benchmark.cpp )
  • opóźnienie hwbindera (dostępne w system/libhwbinder/vts/performance/Latency.cpp )

O spoiwie i hwbinderze

Binder i hwbinder to infrastruktury komunikacji międzyprocesowej (IPC) systemu Android, które korzystają z tego samego sterownika dla systemu Linux, ale mają następujące różnice jakościowe:

Aspekt spoiwo hwbinder
Zamiar Zapewnij schemat IPC ogólnego przeznaczenia dla frameworka Komunikuj się ze sprzętem
Nieruchomość Zoptymalizowany pod kątem użycia platformy Android Minimalne obciążenie i niskie opóźnienia
Zmień zasady planowania dla pierwszego/tła Tak NIE
Argumenty przemijają Wykorzystuje serializację obsługiwaną przez obiekt Parcel Używa buforów rozproszenia i pozwala uniknąć narzutu związanego z kopiowaniem danych wymaganych do serializacji Parcel
Dziedziczenie priorytetowe NIE Tak

Procesy wiążące i hwbinderowe

Wizualizator systrace wyświetla transakcje w następujący sposób:

Rysunek 1. Wizualizacja Systrace procesów wiążących.

W powyższym przykładzie:

  • Cztery (4) procesy schd-dbg są procesami klienckimi.
  • Cztery (4) procesy bindera są procesami serwera (nazwa zaczyna się od Binder i kończy numerem sekwencyjnym).
  • Proces klienta jest zawsze powiązany z procesem serwera, który jest dedykowany jego klientowi.
  • Wszystkie pary procesów klient-serwer są zaplanowane niezależnie przez jądro i jednocześnie.

W CPU 1 jądro systemu operacyjnego uruchamia klienta w celu wysłania żądania. Następnie, jeśli to możliwe, używa tego samego procesora, aby obudzić proces serwera, obsłużyć żądanie i ponownie przełączyć kontekst po zakończeniu żądania.

Przepustowość a opóźnienie

W idealnej transakcji, w której proces klienta i serwera przełączają się płynnie, testy przepustowości i opóźnień nie dają zasadniczo różnych komunikatów. Jednakże, gdy jądro systemu operacyjnego obsługuje żądanie przerwania (IRQ) ze sprzętu, czeka na blokady lub po prostu decyduje się nie natychmiastowo obsługiwać komunikat, może powstać bańka opóźnienia.

Rysunek 2. Bańka opóźnień wynikająca z różnic w przepustowości i opóźnieniach.

Test przepustowości generuje dużą liczbę transakcji o różnych rozmiarach ładunku, zapewniając dobre oszacowanie zwykłego czasu transakcji (w najlepszych scenariuszach) i maksymalnej przepustowości, jaką może osiągnąć segregator.

Natomiast test opóźnienia nie wykonuje żadnych działań na ładunku, aby zminimalizować zwykły czas transakcji. Możemy wykorzystać czas transakcji do oszacowania narzutu związanego z segregatorem, stworzyć statystyki dla najgorszego przypadku i obliczyć współczynnik transakcji, których opóźnienie przekracza określony termin.

Obsługuj inwersje priorytetów

Odwrócenie priorytetu ma miejsce, gdy wątek o wyższym priorytecie logicznie czeka na wątek o niższym priorytecie. W aplikacjach czasu rzeczywistego (RT) występuje problem inwersji priorytetów:

Rysunek 3. Odwrócenie priorytetów w aplikacjach czasu rzeczywistego.

Podczas korzystania z planowania w systemie Linux Completely Fair Scheduler (CFS) wątek zawsze ma szansę na uruchomienie, nawet jeśli inne wątki mają wyższy priorytet. W rezultacie aplikacje korzystające z harmonogramu CFS traktują inwersję priorytetów zgodnie z oczekiwaniami, a nie jako problem. W przypadkach, gdy środowisko Androida wymaga planowania RT, aby zagwarantować przywilej wątków o wysokim priorytecie, należy rozwiązać problem odwrócenia priorytetów.

Przykładowe odwrócenie priorytetów podczas transakcji segregatora (wątek RT jest logicznie blokowany przez inne wątki CFS podczas oczekiwania na obsługę wątku segregatora):

Rysunek 4. Odwrócenie priorytetów, zablokowane wątki czasu rzeczywistego.

Aby uniknąć blokad, możesz użyć dziedziczenia priorytetów, aby tymczasowo eskalować wątek Binder do wątku RT, gdy obsługuje on żądanie od klienta RT. Należy pamiętać, że planowanie czasu rzeczywistego ma ograniczone zasoby i należy z niego korzystać ostrożnie. W systemie z n procesorami maksymalna liczba bieżących wątków RT również wynosi n ; dodatkowe wątki RT mogą wymagać poczekania (a tym samym przekroczenia terminów), jeśli wszystkie procesory zostaną zajęte przez inne wątki RT.

Aby rozwiązać wszystkie możliwe inwersje priorytetów, możesz użyć dziedziczenia priorytetów zarówno dla bindera, jak i hwbindera. Jednakże, ponieważ segregator jest szeroko stosowany w całym systemie, włączenie dziedziczenia priorytetów dla transakcji segregatorów może spamować system większą liczbą wątków RT, niż jest w stanie obsłużyć.

Uruchom testy przepustowości

Test przepustowości jest uruchamiany względem przepustowości transakcji binder/hwbinder. W systemie, który nie jest przeciążony, bąbelki opóźnień są rzadkie i ich wpływ można wyeliminować, o ile liczba iteracji jest odpowiednio duża.

  • Test przepustowości segregatora znajduje się w system/libhwbinder/vts/performance/Benchmark_binder.cpp .
  • Test przepustowości hwbindera znajduje się w system/libhwbinder/vts/performance/Benchmark.cpp .

Wyniki testu

Przykładowe wyniki testu przepustowości dla transakcji wykorzystujących różne rozmiary ładunku:

Benchmark                      Time          CPU           Iterations
---------------------------------------------------------------------
BM_sendVec_binderize/4         70302 ns      32820 ns      21054
BM_sendVec_binderize/8         69974 ns      32700 ns      21296
BM_sendVec_binderize/16        70079 ns      32750 ns      21365
BM_sendVec_binderize/32        69907 ns      32686 ns      21310
BM_sendVec_binderize/64        70338 ns      32810 ns      21398
BM_sendVec_binderize/128       70012 ns      32768 ns      21377
BM_sendVec_binderize/256       69836 ns      32740 ns      21329
BM_sendVec_binderize/512       69986 ns      32830 ns      21296
BM_sendVec_binderize/1024      69714 ns      32757 ns      21319
BM_sendVec_binderize/2k        75002 ns      34520 ns      20305
BM_sendVec_binderize/4k        81955 ns      39116 ns      17895
BM_sendVec_binderize/8k        95316 ns      45710 ns      15350
BM_sendVec_binderize/16k      112751 ns      54417 ns      12679
BM_sendVec_binderize/32k      146642 ns      71339 ns       9901
BM_sendVec_binderize/64k      214796 ns     104665 ns       6495
  • Czas wskazuje opóźnienie podróży w obie strony mierzone w czasie rzeczywistym.
  • Procesor wskazuje skumulowany czas, w którym zaplanowano test procesorów.
  • Iteracje wskazują, ile razy została wykonana funkcja testowa.

Na przykład dla ładunku 8-bajtowego:

BM_sendVec_binderize/8         69974 ns      32700 ns      21296

…maksymalną przepustowość, jaką może osiągnąć segregator, oblicza się jako:

MAKS. przepustowość przy 8-bajtowym ładunku = (8 * 21296)/69974 ~= 2,423 b/ns ~= 2,268 Gb/s

Opcje testowe

Aby uzyskać wyniki w formacie .json, uruchom test z argumentem --benchmark_format=json :

libhwbinder_benchmark --benchmark_format=json
{
  "context": {
    "date": "2017-05-17 08:32:47",
    "num_cpus": 4,
    "mhz_per_cpu": 19,
    "cpu_scaling_enabled": true,
    "library_build_type": "release"
  },
  "benchmarks": [
    {
      "name": "BM_sendVec_binderize/4",
      "iterations": 32342,
      "real_time": 47809,
      "cpu_time": 21906,
      "time_unit": "ns"
    },
   ….
}

Uruchom testy opóźnień

Test opóźnienia mierzy czas potrzebny klientowi na rozpoczęcie inicjowania transakcji, przejście do procesu serwera w celu obsługi i otrzymanie wyniku. Test szuka również znanych nieprawidłowych zachowań programu planującego, które mogą negatywnie wpłynąć na opóźnienia transakcji, na przykład programu planującego, który nie obsługuje dziedziczenia priorytetów ani nie honoruje flagi synchronizacji.

  • Test opóźnienia segregatora znajduje się w frameworks/native/libs/binder/tests/schd-dbg.cpp .
  • Test opóźnienia hwbindera znajduje się w system/libhwbinder/vts/performance/Latency.cpp .

Wyniki testu

Wyniki (w formacie .json) przedstawiają statystyki dotyczące średniego/najlepszego/najgorszego opóźnienia oraz liczby niedotrzymanych terminów.

Opcje testowe

Testy opóźnień obejmują następujące opcje:

Komenda Opis
-i value Określ liczbę iteracji.
-pair value Określ liczbę par procesów.
-deadline_us 2500 Określ w nas termin.
-v Uzyskaj szczegółowe dane wyjściowe (debugowania).
-trace Zatrzymaj śledzenie po osiągnięciu ostatecznego terminu.

W poniższych sekcjach szczegółowo opisano każdą opcję, opisano użycie i przedstawiono przykładowe wyniki.

Określ iteracje

Przykład z dużą liczbą iteracji i wyłączonymi szczegółowymi wynikami:

libhwbinder_latency -i 5000 -pair 3
{
"cfg":{"pair":3,"iterations":5000,"deadline_us":2500},
"P0":{"SYNC":"GOOD","S":9352,"I":10000,"R":0.9352,
  "other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996},
  "fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
},
"P1":{"SYNC":"GOOD","S":9334,"I":10000,"R":0.9334,
  "other_ms":{ "avg":0.19, "wst":2.9 , "bst":0.055, "miss":2, "meetR":0.9996},
  "fifo_ms": { "avg":0.16, "wst":3.1 , "bst":0.066, "miss":1, "meetR":0.9998}
},
"P2":{"SYNC":"GOOD","S":9369,"I":10000,"R":0.9369,
  "other_ms":{ "avg":0.19, "wst":4.8 , "bst":0.055, "miss":6, "meetR":0.9988},
  "fifo_ms": { "avg":0.15, "wst":1.8 , "bst":0.067, "miss":0, "meetR":1}
},
"inheritance": "PASS"
}

Wyniki testu pokazują, co następuje:

"pair":3
Tworzy jedną parę klient-serwer.
"iterations": 5000
Obejmuje 5000 iteracji.
"deadline_us":2500
Termin ostateczny to 2500 us (2,5 ms); oczekuje się, że większość transakcji osiągnie tę wartość.
"I": 10000
Pojedyncza iteracja testu obejmuje dwie (2) transakcje:
  • Jedna transakcja o normalnym priorytecie ( CFS other )
  • Jedna transakcja według priorytetu czasu rzeczywistego ( RT-fifo )
5000 iteracji równa się sumie 10000 transakcji.
"S": 9352
9352 transakcji jest synchronizowanych w tym samym procesorze.
"R": 0.9352
Wskazuje stopień synchronizacji klienta i serwera w tym samym procesorze.
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996}
Średni ( avg ), najgorszy ( wst ) i najlepszy ( bst ) przypadek wszystkich transakcji wydanych przez osobę wywołującą o normalnym priorytecie. Dwie transakcje miss terminu, przez co współczynnik spełnienia ( meetR ) wynosi 0,9996.
"fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
Podobny do other_ms , ale dla transakcji wystawianych przez klienta z priorytetem rt_fifo . Jest prawdopodobne (ale nie wymagane), że fifo_ms ma lepszy wynik niż other_ms , z niższymi wartościami avg i wst oraz wyższym meetR (różnica może być jeszcze bardziej znacząca przy obciążeniu w tle).

Uwaga: obciążenie w tle może mieć wpływ na wynik przepustowości i krotkę other_ms w teście opóźnienia. Tylko fifo_ms może pokazać podobne wyniki, o ile ładowanie w tle ma niższy priorytet niż RT-fifo .

Określ wartości par

Każdy proces klienta jest sparowany z procesem serwera dedykowanym dla klienta, a każda para może być planowana niezależnie dla dowolnego procesora. Jednak migracja procesora nie powinna mieć miejsca podczas transakcji, jeśli flaga SYNC ma honor .

Upewnij się, że system nie jest przeciążony! Chociaż w przeciążonym systemie oczekuje się dużych opóźnień, wyniki testów dla przeciążonego systemu nie dostarczają użytecznych informacji. Aby przetestować system przy wyższym ciśnieniu, użyj -pair #cpu-1 (lub -pair #cpu zachowując ostrożność). Testowanie przy użyciu -pair n z n > #cpu przeciąża system i generuje bezużyteczne informacje.

Określ wartości ostatecznych terminów

Po szeroko zakrojonych testach scenariuszy z użytkownikami (przeprowadzeniu testu opóźnienia na zakwalifikowanym produkcie) ustaliliśmy, że ostatecznym terminem do dotrzymania jest 2,5 ms. W przypadku nowych aplikacji o wyższych wymaganiach (np. 1000 zdjęć/sekundę) ta wartość terminu ulegnie zmianie.

Określ szczegółowe dane wyjściowe

Użycie opcji -v powoduje wyświetlenie pełnych wyników. Przykład:

libhwbinder_latency -i 1 -v

-------------------------------------------------- service pid: 8674 tid: 8674 cpu: 1 SCHED_OTHER 0
-------------------------------------------------- main pid: 8673 tid: 8673 cpu: 1 -------------------------------------------------- client pid: 8677 tid: 8677 cpu: 0 SCHED_OTHER 0
-------------------------------------------------- fifo-caller pid: 8677 tid: 8678 cpu: 0 SCHED_FIFO 99 -------------------------------------------------- hwbinder pid: 8674 tid: 8676 cpu: 0 ??? 99
-------------------------------------------------- other-caller pid: 8677 tid: 8677 cpu: 0 SCHED_OTHER 0 -------------------------------------------------- hwbinder pid: 8674 tid: 8676 cpu: 0 SCHED_OTHER 0
  • Wątek usługi jest tworzony z priorytetem SCHED_OTHER i uruchamiany w CPU:1 z pid 8674 .
  • Pierwsza transakcja jest następnie rozpoczynana przez fifo-caller . Aby obsłużyć tę transakcję, hwbinder podnosi priorytet serwera ( pid: 8674 tid: 8676 ) do 99, a także oznacza go klasą planowania przejściowego (drukowaną jako ??? ). Następnie program planujący umieszcza proces serwera w CPU:0 aby mógł zostać uruchomiony, i synchronizuje go z tym samym procesorem, co jego klient.
  • Drugi obiekt wywołujący transakcję ma priorytet SCHED_OTHER . Serwer sam obniża wersję i obsługuje obiekt wywołujący z priorytetem SCHED_OTHER .

Użyj śledzenia do debugowania

Można określić opcję -trace , aby debugować problemy z opóźnieniami. Gdy jest używany, test opóźnienia zatrzymuje zapis dziennika śledzenia w momencie wykrycia złego opóźnienia. Przykład:

atrace --async_start -b 8000 -c sched idle workq binder_driver sync freq
libhwbinder_latency -deadline_us 50000 -trace -i 50000 -pair 3
deadline triggered: halt ∓ stop trace
log:/sys/kernel/debug/tracing/trace

Następujące komponenty mogą mieć wpływ na opóźnienia:

  • Tryb kompilacji Androida . Tryb Eng jest zwykle wolniejszy niż tryb debugowania użytkownika.
  • Ramy . W jaki sposób usługa frameworka używa ioctl do konfiguracji w segregatorze?
  • Sterownik segregatora . Czy sterownik obsługuje precyzyjne blokowanie? Czy zawiera wszystkie poprawki poprawiające wydajność?
  • Wersja jądra . Im lepsze możliwości działania w czasie rzeczywistym posiada jądro, tym lepsze wyniki.
  • Konfiguracja jądra . Czy konfiguracja jądra zawiera konfiguracje DEBUG , takie jak DEBUG_PREEMPT i DEBUG_SPIN_LOCK ?
  • Harmonogram jądra . Czy jądro ma harmonogram uwzględniający energię (EAS) lub harmonogram heterogenicznego przetwarzania wieloprocesowego (HMP)? Czy jakieś sterowniki jądra (sterownik cpu-freq , sterownik cpu-idle , cpu-hotplug itp.) wpływają na harmonogram?