Studium przypadku: jak zespół Gmaila Wear OS poprawił swoje uruchamianie aplikacji o 50%

Uruchomienie aplikacji to pierwsze wrażenie, jakie użytkownicy wywierają na aplikacji. Użytkownicy nie lubią czekać, więc musisz zadbać o to, by aplikacja szybko się uruchamiała. Poniżej znajdziesz opis czynności, które zespół Gmaila na Wear OS wykrył i zdiagnozował problemy z uruchamianiem aplikacji.

Zespół Gmaila Wear OS podjął działania optymalizacyjne, koncentrując się w szczególności na uruchamianiu aplikacji i renderowaniu w czasie działania, aby spełnić wymagania dotyczące wydajności aplikacji. Jednak nawet wtedy, gdy nie masz wyznaczonego konkretnego progu, niemal zawsze możesz spróbować poprawić działanie aplikacji, jeśli poświęcisz trochę czasu na jej przeanalizowanie.

Rejestrowanie logu czasu i sprawdzanie uruchamiania aplikacji

Aby rozpocząć analizę, przechwyć log czasu obejmujący uruchomienie aplikacji w celu dokładniejszej analizy w Perfetto lub Android Studio. To studium przypadku wykorzystuje Perfetto, ponieważ pokazuje, co dzieje się w systemie urządzenia, a nie w samej aplikacji. Gdy prześlesz ślad w Perfetto, wygląda to tak:

Rysunek 1. Wstępny widok logu czasu w Perfetto.

Głównym celem jest usprawnienie uruchamiania aplikacji, znajdź wiersz z danymi niestandardowymi Android App Startups. Warto przypiąć go u góry widoku, klikając ikonę pinezki , która pojawi się po najechaniu kursorem na wiersz. Pasek (wycinek) widoczny w wierszu Uruchamianie aplikacji na Androida wskazuje przedział czasu, przez jaki obejmuje uruchamianie aplikacji do momentu wyświetlenia na ekranie pierwszej klatki aplikacji. Warto więc sprawdzić, czy nie występują w nim problemy lub wąskie gardła.

Wiersz uruchamiania aplikacji na Androida z wyróżnioną opcją przypięcia.
Rysunek 2. Przypnij dane niestandardowe uruchamiania aplikacji na Androida na górze panelu, aby ułatwić sobie analizę.

Pamiętaj, że dane Uruchomienia aplikacji na Androida pokazują czas do początkowego wyświetlenia, nawet jeśli używasz reportFullyDrawn(). Aby określić czas do pełnego wyświetlenia, w polu wyszukiwania Perfetto wyszukaj reportFullyDrawn().

Sprawdź wątek główny

Najpierw sprawdź, co dzieje się w wątku głównym. Wątek główny jest bardzo ważny, ponieważ to w nim zwykle odbywa się renderowanie interfejsu. Gdy jest zablokowany, nie można rysować, a aplikacja wydaje się zablokowana. Chcesz mieć pewność, że w wątku głównym nie będą prowadzone żadne długo trwające operacje.

Aby znaleźć wątek główny, znajdź wiersz z nazwą pakietu aplikacji i go rozwiń. Dwa wiersze o tej samej nazwie co pakiet (zwykle są to 2 pierwsze wiersze sekcji) reprezentują wątek główny. Pierwszy z 2 wierszy wątków głównych reprezentuje stan procesora, a drugi – punkty śledzenia. Przypnij 2 główne wiersze z wątkami poniżej danych Uruchomienia aplikacji na Androida.

Uruchamianie aplikacji na Androida i wiersze z wątkami głównymi zostały przypięte.
Rysunek 3. Aby ułatwić analizę, przypnij wiersze głównych wątków pod danymi niestandardowymi związanymi z uruchamianiem aplikacji na Androida.

Czas w stanie uruchomienia i rywalizacja z procesorem

Aby wyświetlić zbiorczy widok aktywności procesora podczas uruchamiania aplikacji, przeciągnij kursor na wątek główny. Pojawi się panel ThreadStates, który pokazuje łączny czas spędzony w poszczególnych stanach procesora w wybranym przedziale czasu.

Spójrz na czas spędzony w stanie Runnable. Gdy wątek ma stan Runnable, jest dostępny do wykonania, ale nie ma zaplanowanych żadnych działań. Może to oznaczać, że urządzenie jest bardzo obciążane i nie jest w stanie zaplanować zadań o wysokim priorytecie. Górna, widoczna dla użytkownika aplikacja ma najwyższy priorytet przy planowaniu, więc bezczynny wątek główny często wskazuje, że intensywne procesy w aplikacji, takie jak renderowanie animacji, konkurują z wątkiem głównym o czas pracy procesora.

Wątek główny wyróżniony w panelu Stany wątków z łącznym czasem w różnych stanach.
Rysunek 4. Oceń względny czas w stanach od Runnable do Running, aby początkowo zorientować się, jaka jest rywalizacja z procesorem.

Im wyższy stosunek czasu w stanie Runnable do czasu w stanie Running, tym większe prawdopodobieństwo, że toczy się rywalizacja z CPU. Analizując problemy z wydajnością w ten sposób, skup się najpierw na najdłużej trwających klatkach, a potem na mniejszych.

Analizując czas w stanie Runnable, weź pod uwagę sprzęt urządzenia. Przedstawiona aplikacja działa na urządzeniu do noszenia z 2 procesorami, więc oczekiwanie dotyczy dłuższego czasu w stanie Runnable i większej rywalizacji procesora z innymi procesami niż w przypadku urządzenia z większą liczbą procesorów. Chociaż stan Runnable wymaga więcej czasu, niż oczekiwano w przypadku typowej aplikacji na telefon, może to być zrozumiałe w kontekście urządzeń do noszenia.

Czas korzystania z aplikacji OpenDexFilesFromOat*

Teraz sprawdź czas spędzony w elemencie OpenDexFilesFromOat*. W śladzie dzieje się to w tym samym czasie co wycinek bindApplication. Ten wycinek reprezentuje czas potrzebny na odczyt plików DEX aplikacji.

Zablokowane transakcje binarne

Następnie sprawdź transakcje segregujące. Transakcje Binder to wywołania między klientem a serwerem: w tym przypadku aplikacja (klient) wywołuje system Android (serwer) za pomocą wywołania binder transaction, a serwer odpowiada binder reply. Upewnij się, że aplikacja nie wykonuje zbędnych transakcji podczas uruchamiania, ponieważ zwiększa to ryzyko rywalizacji z procesorem. Jeśli możesz, odłóż zadania, które obejmują wywołania powiązania, po okresie uruchamiania aplikacji. Jeśli musisz dokonywać powiązanych transakcji, upewnij się, że nie trwają one dłużej niż częstotliwość odświeżania vsync na Twoim urządzeniu.

Pierwsza transakcja łącząca, zwykle odbywająca się w tym samym czasie co wycinek ActivityThreadMain, wydaje się w tym przypadku dość długa. Aby dowiedzieć się, co może się zdarzyć, wykonaj te czynności:

  1. Aby wyświetlić powiązaną odpowiedź powiązaną z powiązaniem i dowiedzieć się więcej o tym, jak ustalana jest priorytet tej transakcji, kliknij powiązany wycinek zainteresowań.
  2. Aby zobaczyć binarną odpowiedź, przejdź do panelu Bieżący wybór i w sekcji Obserwowane wątki kliknij binder response (Odpowiedź Binder). Pole Wątek zawiera też informację o wątku, w którym znajduje się odpowiedź pośrednia, jeśli chcesz przejść do niego ręcznie – będzie to inny proces. Pojawi się wiersz łączący transakcję Binder i odpowiedź.

    Linia łączy wywołanie Binder i odpowiedź.
    Rysunek 5. Zidentyfikuj powiązane transakcje podczas uruchamiania aplikacji i oceń, czy można je odroczyć.
  3. Aby sprawdzić, jak serwer systemu obsługuje tę transakcję powiązaną, przypnij wątki CPU 0 i CPU 1 u góry ekranu.

  4. Znajdź procesy systemowe obsługujące odpowiedź Binder, znajdując wycinki zawierające nazwę wątku tego powiązania, w tym przypadku „Binder:687_11 [2542]”. Kliknij odpowiednie procesy systemowe, aby uzyskać więcej informacji o transakcji powiązanej.

Spójrz na ten proces systemowy powiązany z transakcją wiążącą udziału, która ma miejsce na procesorze 0:

Proces systemowy o stanie końcowym „uruchomiony (wywłaszczony).
Rysunek 6. Proces systemowy jest w stanie Runnable (Preempted), co oznacza, że jest opóźniony.

Stan końcowy zawiera wartość Runnable (Preempted), co oznacza, że proces jest opóźniony, ponieważ procesor robi coś innego. Aby sprawdzić, przez co zostaje wywłaszczony, rozwiń wiersze Zdarzenia w Ftrace. Na wyświetlonej karcie FtraceEvents (Zdarzenia Ftrace) przewiń w dół i poszukaj zdarzeń związanych z interesującym Cię wątkiem „Binder:687_11 [2542]”. W trakcie wywłaszczania procesu systemowego wystąpiły 2 zdarzenia serwera systemowego zawierające argument „decon”, co oznacza, że są one powiązane z kontrolerem ekranu. Brzmi to rozsądnie, ponieważ kontroler wyświetlacza umieszcza klatki na ekranie – to ważne zadanie. Do tego czasu pozostaw wydarzenia bez zmian.

Zdarzenia FTrace powiązane z wyróżnioną transakcją wiążącą zainteresowania.
Rysunek 7. Zdarzenia FTrace wskazują, że transakcja powiązania jest opóźniona przez zdarzenia kontrolera wyświetlacza.

aktywność JIT

Aby zbadać aktywność kompilacji w czasie (JIT), rozwiń procesy należące do Twojej aplikacji, znajdź 2 wiersze „Pula wątków Jit” i przypnij je na górze widoku. Ponieważ podczas uruchamiania aplikacja korzysta z profili podstawowych, aktywność JIT jest bardzo mała, dopóki nie zostanie pobrana pierwsza klatka, co oznacza koniec jej pierwszego wywołania Choreographer.doFrame. Zwróć jednak uwagę na przyczynę powolnego uruchomienia JIT compiling void, co sugeruje, że działania systemu wykonywane w punkcie śledzenia o nazwie Application creation powodują dużą aktywność JIT w tle. Aby rozwiązać ten problem, dodaj do profilu podstawowego zdarzenia, które nastąpiły wkrótce po tym, jak pierwsza klatka zostanie pobrana, rozwijając kolekcję profili do punktu, w którym aplikacja jest gotowa do użycia. W wielu przypadkach można to zrobić, dodając wiersz na końcu kolekcji profilu podstawowego. Test porównawczy Macrobenchmark czeka na pojawienie się określonego widżetu interfejsu na ekranie. Oznacza to, że ekran jest całkowicie wypełniony.

Pule wątków Jit z wyróżnionym wycinkiem „Jit Comppiling void”.
Rysunek 8. Jeśli zauważysz dużą aktywność przy użyciu metody JIT, rozwiń swój profil podstawowy do punktu, w którym aplikacja jest gotowa do użycia.

Wyniki

W wyniku tej analizy zespół Gmaila na Wear OS wprowadził następujące ulepszenia:

  • Ponieważ zauważono rywalizację podczas uruchamiania aplikacji na aktywności procesora, zastąpili animację wskaźnika postępu ładowania aplikacji jednym statycznym obrazem. Wydłużyli też ekran powitalny, aby opóźnić powstawanie migotania, czyli drugi stan ekranu wskazujący, że aplikacja się wczytuje. To skróciło czas oczekiwania na uruchomienie aplikacji o 50%.
  • Analiza czasu spędzonego w OpenDexFilesFromOat* i aktywności w JIT umożliwiła przepisywanie profili podstawowych dla systemu R8. Skróciło to czas oczekiwania na uruchomienie aplikacji o 20%.

Oto kilka wskazówek od zespołu dotyczących efektywnego analizowania wydajności aplikacji:

  • Skonfiguruj działający proces, który może automatycznie zbierać logi czasu i wyniki. Rozważ skonfigurowanie automatycznego śledzenia aplikacji za pomocą analizy porównawczej.
  • Przeprowadzaj testy A/B, aby sprawdzać zmiany, które Twoim zdaniem wpłyną na poprawę, i odrzucaj je, jeśli nie są. Skuteczność możesz mierzyć w różnych scenariuszach za pomocą biblioteki Macrobenchmark.

Więcej informacji znajdziesz w tych materiałach: