Omówienie pomiaru skuteczności aplikacji

Ten dokument pomoże Ci zidentyfikować i rozwiązać kluczowe problemy z wydajnością aplikacji.

Najważniejsze problemy z wydajnością

Istnieje wiele problemów, które mogą przyczyniać się do słabej skuteczności aplikacji, ale oto kilka typowych problemów, które mogą wystąpić w Twojej aplikacji:

Czas oczekiwania na uruchomienie

Czas oczekiwania na uruchomienie to czas, jaki upływa między kliknięciem aplikacji ikonę, powiadomienie lub inny punkt wejścia, a także dane użytkownika ekranu.

W swoich aplikacjach dążyć do osiągnięcia tych celów dotyczących uruchamiania:

  • Uruchomienie „na zimno” w czasie krótszym niż 500 ms. Uruchomienie „na zimno” ma miejsce, gdy uruchamiana aplikacja nie jest obecna w pamięci systemu. Dzieje się tak, gdy aplikacja jest uruchamiana po raz pierwszy od ponownego uruchomienia lub zatrzymania procesu przez użytkownika lub system.

    Uruchomienie częściowo z pamięci następuje wtedy, gdy aplikacja jest już uruchomiona na w tle. Uruchomienie „na zimno” wymaga największego nakładu pracy systemu, ponieważ musi wczytać wszystko z pamięci i zainicjować aplikację. Spróbuj uruchomienia „na zimno” trwają maksymalnie 500 ms.

  • Czasy oczekiwania P95 i P99 są bardzo zbliżone do mediany. Gdy aplikacja uruchamianie zajmuje sporo czasu, co źle wpływa na wrażenia użytkowników. Komunikacja między procesami (IPC) i niepotrzebne operacje wejścia/wyjścia na ścieżce krytycznej podczas uruchamiania aplikacji mogą powodować rywalizację o blokadę i niespójności.

Jankowanie podczas przewijania

Jank to termin opisujący czkawkę, która występuje, gdy system nie jest w stanie skompilować i dostarczyć klatek w odpowiednim czasie, aby wyświetlić je ekranu z żądaną częstotliwością 60 Hz lub większą. Problem jest najbardziej widoczny podczas przewijania, gdy zamiast płynnej animacji występują zacięcia. Janek pojawia się, gdy ruch zostanie zatrzymany w trakcie co najmniej jednej klatki, czas renderowania treści przez aplikację trwa dłużej niż długość klatki w systemie.

Aplikacje muszą być kierowane na częstotliwość odświeżania 90 Hz. Konwencjonalne częstotliwości renderowania wynoszą 60 Hz, ale wiele nowszych urządzeń działa w trybie 90 Hz podczas interakcji z użytkownikiem, np. przewijania. Niektóre urządzenia obsługują nawet wyższe częstotliwości do 120 Hz.

Aby sprawdzić, z jakiej częstotliwości odświeżania korzysta urządzenie w danym momencie, włącz nakładkę, korzystając z opcji Opcje programisty > Pokaż częstotliwość odświeżania w sekcji Debugowanie.

Przejścia, które nie są płynne

Jest to widoczne podczas interakcji, takich jak przełączanie się między kartami czy wczytywanie. nową aktywność. Te typy przejść muszą być płynnymi animacjami bez opóźnień ani migotania.

Nieefektywne wykorzystanie energii

Wykonywanie zadań powoduje zużycie baterii, a wykonywanie niepotrzebnych zadań skraca czas jej pracy.

Przydzielanie pamięci, które wynika z tworzenia nowych obiektów w kodzie, może być przyczyną znacznego obciążenia systemu. Dzieje się tak, ponieważ nie tylko same alokacje wymagają pracy od środowiska wykonawczego Androida (ART), ale także zwolnienie tych obiektów później (zbieranie zbędących zasobów) wymaga czasu i wysiłku. Przydział i zbieranie są znacznie szybsze i skuteczniejsze, zwłaszcza w przypadku obiektów tymczasowych. Chociaż w przeszłości zalecaliśmy unikanie przydzielania obiektów, obecnie zalecamy, aby robić to w sposób najbardziej odpowiedni dla Twojej aplikacji i jej architektury. Oszczędzanie na alokacjach kosztem możliwości utrzymania kodu nie jest dobrą praktyką, biorąc pod uwagę możliwości ART.

Wymaga to jednak pracy, dlatego należy pamiętać, że może przyczynić się przy przydzieleniu wielu obiektów w wewnętrznej pętli.

Zidentyfikuj problemy

Aby wykryć i rozwiązać problemy ze skutecznością, zalecamy skorzystanie z tego procesu:

  1. Zidentyfikuj i sprawdź te kluczowe ścieżki użytkownika:
    • Typowe procesy uruchamiania, w tym z programu uruchamiającego i powiadomień.
    • Ekrany, na których użytkownik przewija dane.
    • Przejścia między ekranami.
    • długotrwałe procesy, takie jak nawigacja lub odtwarzanie muzyki.
  2. Sprawdzanie, co dzieje się podczas poprzednich przepływów, za pomocą tych narzędzi do debugowania:
    • Perfetto: pozwala zobaczyć, co dzieje się na całym urządzeniu, z dokładnymi danymi o czasie.
    • Program profilujący pamięci: umożliwia sprawdzanie przeprowadzanych alokacji pamięci na stercie.
    • Simpleperf: wykres flamegraficzny pokazujący, które wywołania funkcji korzystają z funkcji z największym wykorzystaniem CPU w danym okresie. Gdy zidentyfikujesz coś w Systrace zajmuje dużo czasu, ale nie wiadomo dlaczego, Simpleperf może zawierać dodatkowe informacje.

Aby zrozumieć te problemy z wydajnością i je debugować, musisz ręcznie debugować poszczególne testy. Nie możesz zastąpić poprzednich kroków przez analizę danych zbiorczych. Aby jednak zrozumieć, co widzą użytkownicy, i stwierdzić, kiedy mogą wystąpić regresje, ważne jest skonfigurowanie zbierania danych w ramach testów automatycznych i w polu:

  • Sekwencje uruchamiania
  • Jank
    • Dane dotyczące pól
      • Oto najważniejsze wskaźniki w Konsoli Play: w Konsoli Play nie można zawęzić do danych o konkretnej ścieżce klienta. Raportuje tylko ogólne bałagan w całej aplikacji.
      • Do pomiarów niestandardowych w usłudze FrameMetricsAggregator: możesz stosować FrameMetricsAggregator, aby rejestrować dane o zacinaniu w danym okresie procesu tworzenia kampanii.
    • Testy laboratoryjne
      • Przewijanie za pomocą makrobenchmarku.
      • Test porównawczy gromadzi dane o czasie renderowania klatek za pomocą poleceń dumpsys gfxinfo które obejmują ścieżkę pojedynczego użytkownika. Dzięki temu możesz poznać różnice w zakolach w przypadku konkretnej ścieżki użytkownika. Dane RenderTime, które wskazują, jak długo trwa renderowanie klatki, są ważniejsze niż liczba klatek z zakłóceniami w przypadku wykrywania regresji lub ulepszeń.

Linki aplikacji to precyzyjne linki oparte na adresie URL Twojej witryny, które zostały zweryfikowane należą do Twojej witryny. Oto powody, dla których link aplikacji może być wyświetlany nie powiodła się.

  • Zakresy filtrów intencji: dodaj do filtrów intencji tylko parametr autoVerify w przypadku adresów URL, które na które aplikacja może odpowiedzieć.
  • Niezweryfikowane przekierowania protokołów: niezweryfikowane przekierowania po stronie serwera i subdomeny są uznawane za zagrożenia dla bezpieczeństwa i nie przejdą weryfikacji. Sprawiają, że wszystkie autoVerify linku nie powiodło się. Na przykład przekierowanie linków z HTTP na HTTPS, np. z example.com na www.example.com, bez weryfikacji linków HTTPS, może spowodować niepowodzenie weryfikacji. Pamiętaj, aby zweryfikować linki aplikacji, dodając intencję filtry.
  • Linki nieweryfikowalne: dodanie do testów sprawia, że system nie weryfikuje linków aplikacji.
  • Niestabilne serwery: upewnij się, że serwery mogą łączyć się z aplikacjami klienckimi.

Konfigurowanie aplikacji pod kątem analizy wydajności

Aby uzyskać dokładne, powtarzalne i przydatne wyniki testów porównawczych aplikacji, musisz odpowiednio skonfigurować system. Testuj na systemie, który jest jak najbardziej zbliżony do środowiska produkcyjnego, jednocześnie eliminując źródła zakłóceń. W następnych sekcjach znajdziesz kilka kroków, które możesz wykonać, aby przygotować konfigurację testową. Niektóre z nich są zależne od przypadku użycia.

punkty śledzenia,

Aplikacje mogą wykorzystywać swój kod do niestandardowych zdarzeń śledzenia.

Podczas rejestrowania śladów generowany jest niewielki narzut około 5 μs na sekcję, dlatego nie należy stosować go w przypadku każdej metody. Śledzenie większych fragmentów czas pracy >0,1 ms może dostarczyć znaczących informacji na temat wąskich gardeł.

Informacje o plikach APK

Warianty debugowania mogą być przydatne podczas rozwiązywania problemów i symbolizowania próbek stosu, ale mają poważny wpływ na wydajność. Na urządzeniach z Androidem 10 (poziom interfejsu API 29) lub nowszym można użyć w pliku manifestu atrybutu profileable android:shell="true", aby włączyć profilowanie w wersjach wydania.

Użyj konfiguracji zmniejszania kodu klasy produkcyjnej. W zależności od zasobów używanych przez aplikację może to mieć znaczny wpływ na wydajność. Niektóre Konfiguracje ProGuard usuwają punkty śledzenia, więc rozważ usunięcie tych reguł dla w konfiguracji, w której prowadzisz testy.

Kompilacja

Zkompiluj aplikację na urządzeniu do znanego stanu – zazwyczaj speed lub speed-profile. Działania w tle typu just-in-time (JIT) mogą mieć znaczny wpływ na wydajność. Często dochodzi do tego, gdy ponownie instalujesz plik APK między kolejnymi testami. Oto polecenie, które pozwala wykonać tę czynność:

adb shell cmd package compile -m speed -f com.google.packagename

Tryb kompilacji speed całkowicie kompiluje aplikację. Tryb speed-profile kompiluje aplikację zgodnie z profilem wykorzystanych ścieżek kodu, który jest zbierany podczas korzystania z aplikacji. Zbieranie profili w sposób spójny i prawidłowy może być trudne, dlatego jeśli zdecydujesz się ich używać, sprawdź, czy zbierają one dane, których oczekujesz. Profile znajdują się w tym miejscu:

/data/misc/profiles/ref/[package-name]/primary.prof

Test porównawczy umożliwia bezpośrednie określenie trybu kompilacji.

Uwagi dotyczące systemu

W przypadku pomiarów o niskiej i wysokiej wierności należy skalibrować urządzenia. Przeprowadzanie testów A/B na tym samym urządzeniu i z użyciem tej samej wersji systemu operacyjnego. Mogą mieć one znaczny wpływ na użytkowników, nawet na urządzeniach tego samego typu.

Na urządzeniach z rootem rozważ użycie skryptu lockClocks do przeprowadzania mikrotestów. Skrypty te wykonują między innymi te działania:

  • Umieść procesory ze stałą częstotliwością.
  • Wyłącz małe rdzenie i skonfiguruj GPU.
  • Wyłącz ograniczanie termiczne.

Nie zalecamy używania skryptu lockClocks do testów związanych z wrażeniami użytkowników, takich jak testowanie uruchamiania aplikacji, testowanie DoU i testowanie płynności, ale może on być niezbędny do zmniejszenia szumu w testach mikrobenchmarków.

Jeśli to możliwe, rozważ użycie platformy testowej, takiej jak Macrobenchmark, która może zmniejszyć szum w pomiarach i zapobiec ich niedokładności.

Powolne uruchamianie aplikacji: niepotrzebna aktywność trampoliny

Aktywność taka może niepotrzebnie wydłużyć czas uruchamiania aplikacji. trzeba wiedzieć, czy jest to możliwe w przypadku Twojej aplikacji. Jak widać w tym przykładowym śladzie, po jednym activityStart następuje od razu kolejny activityStart, bez wyświetlania żadnych klatek przez pierwsze działanie.

tekst_alternatywny Rysunek 1. Ślad pokazujący aktywność trampoliny.

Może się to zdarzyć zarówno w punkcie wejścia powiadomienia, jak i podczas zwykłego uruchamiania aplikacji. i często można rozwiązać ten problem przez refaktoryzację. Jeśli na przykład używasz tej czynności do wykonania konfiguracji przed uruchomieniem innej czynności, umieść ten kod w komponencie lub bibliotece wielokrotnego użytku.

Niepotrzebne przypisania wywołujące częste GC

W pliku Systrace możesz zauważyć, że usuwanie elementów zbędnych (GC) występuje częściej, niż się spodziewasz.

W poniższym przykładzie co 10 sekund podczas długo trwającej operacji jest może wskazywać, że aplikacja może niepotrzebnie, ale regularnie przydzielać godzina:

tekst_alternatywny Rysunek 2. Zrzut przedstawiający odstęp między zdarzeniami GC.

Możesz też zauważyć, że podczas korzystania z profilatora pamięci określony stos wywołań odpowiada za zdecydowaną większość alokacji. Nie musisz wyeliminować wszystkich alokacje, ponieważ może to utrudniać zarządzanie kodem. Zamiast tego zacznij od obszarów o największym natężeniu alokacji.

Niestabilne klatki

Proces tworzenia grafiki jest dość skomplikowany i mogą występować pewne niuanse co pozwala określić, czy użytkownik widzi upuszczenie klatki. W niektórych przypadkach platforma może „ratować” kadr, stosując buforowanie. Możesz jednak zignorować większość tych niuansów, aby zidentyfikować problematyczne klatki z perspektywy aplikacji.

Gdy rysowanie klatek wymaga od aplikacji niewielkiej ilości pracy, punkty śladuChoreographer.doFrame() występują co 16,7 ms na urządzeniu z częstotliwością 60 FPS:

alt_text Rysunek 3. Ślad pokazujący częste szybkie klatki.

Po pomniejszeniu widoku i poruszaniu się po logu czasu klatki w celu pobrania jeszcze trochę, ale nic nie szkodzi, bo nie trwa niż przydzielony czas 16,7 ms:

tekst_alternatywny Rysunek 4. Ślad pokazujący częste szybkie klatki z okresowymi seriami pracy.

Jeśli zauważysz przerwę w tym regularnym rytmie, oznacza to, że występuje klatka z niestabilnym obrazem, jak pokazano na rysunku 5:

alt_text Rysunek 5. Ślad przedstawiający klatkę z zakłóceniem.

Możesz przećwiczyć ich rozpoznawanie.

tekst_alternatywny Rysunek 6. Ślad przedstawiający więcej klatek z problemami.

W niektórych przypadkach trzeba powiększyć punkt śledzenia, aby uzyskać więcej informacji które liczby wyświetleń są zawyżone lub co robi RecyclerView. W innych przypadkach konieczne może być dokładniejsze sprawdzenie.

Więcej informacji na temat identyfikowania nieprawidłowych klatek i debugowania ich przyczyn znajdziesz w artykule Więcej informacji znajdziesz w artykule Powolne renderowanie.

Typowe błędy związane z RecyclerView

Niepotrzebne unieważnianie wszystkich danych pomocniczych RecyclerView może prowadzić do długiego czasu renderowania klatek i problemów z płynnością. Zamiast tego, aby zminimalizować liczbę w widokach, które wymagają aktualizacji, unieważni tylko te dane, które uległy zmianie.

Więcej informacji o unikaniu kosztownych wywołań funkcji notifyDatasetChanged(), które powodują aktualizację treści zamiast ich całkowitego zastąpienia, znajdziesz w artykule Przedstawianie danych dynamicznych.

Jeśli nie obsługujesz prawidłowo wszystkich zagnieżdżonych RecyclerView, może to spowodować, że wewnętrzny RecyclerView będzie za każdym razem całkowicie odtwarzany. Każdy zagnieżdżony, wewnętrzny RecyclerView musi mieć ustawiony parametr RecycledViewPool, aby zapewnić które można poddać recyklingowi między wszystkimi wewnętrznymi elementami RecyclerView.

Jeśli dane nie są pobierane z wyprzedzeniem lub w odpowiednim czasie, dotykanie końca listy przewijanej, gdy użytkownik musi czekać z serwera. Technicznie nie jest to zacięte, bo brak ramki możesz znacznie poprawić wygodę użytkowników, modyfikując z czasem i ilością pobierania z wyprzedzeniem, tak by użytkownik nie musiał czekać, i skalowalnych danych.

Debugowanie aplikacji

Oto różne metody debugowania wydajności aplikacji. Obejrzyj ten film, aby dowiedzieć się więcej o śledzeniu systemu i profilowaniu w Android Studio.

Debugowanie uruchamiania aplikacji przy użyciu Systrace

Informacje o czasie uruchamiania aplikacji znajdziesz w artykule Czas uruchamiania aplikacji, a omówienie śledzenia systemu znajdziesz w tym filmie.

Typy startupów możesz rozróżnić na następujących etapach:

  • „Zimny start” – zacznij od utworzenia nowego procesu bez zapisanego stanu.
  • Uruchomienie z pamięci: odtwarza aktywność podczas ponownego wykorzystania procesu lub odtwarza proces z zapisanym stanem.
  • Uruchomienie z pamięci: powoduje ponowne uruchomienie aktywności i rozpoczyna się od inflacji.

Zalecamy przechwytywanie kodu przy użyciu aplikacji do śledzenia systemu na urządzeniu. W przypadku Androida 10 lub nowszego użyj Perfetto. W przypadku Androida 9 lub starszego użyj Systrace. Zalecamy również przeglądanie plików śledzenia za pomocą narzędzia internetowego Przeglądarka śledzenia Perfetto. Więcej informacji znajdziesz w artykule Omówienie śledzenia systemu.

Oto kilka rzeczy, na które warto zwrócić uwagę:

  • Monitorowanie rywalizacji: rywalizacja o zasoby chronione monitorem może spowodować znaczne opóźnienie uruchomienia aplikacji.
  • Synchroniczne transakcje Binder: szukaj niepotrzebnych transakcji na na ścieżce krytycznej aplikacji. Jeśli wymagana transakcja jest kosztowna, rozważ z odpowiednim zespołem ds. platformy w celu wprowadzenia ulepszeń.

  • Jednoczesne GC: jest to częsty i względnie niewielki problem, ale jeśli często się pojawia, rozważ użycie profilowania pamięci w Android Studio.

  • Wejścia/wyjścia: sprawdź, czy podczas uruchamiania występują operacje wejścia-wyjścia, i sprawdź, czy występują długie przerwy.

  • Znacząca aktywność w innych wątkach: może ona zakłócać działanie wątku interfejsu użytkownika, dlatego zwracaj uwagę na działanie w tle podczas uruchamiania.

Aby poprawić raportowanie danych o uruchamianiu aplikacji, zalecamy wywołanie funkcji reportFullyDrawn po zakończeniu uruchamiania aplikacji. Sprawdź godzinę na pełny ekran, aby dowiedzieć się więcej o korzystaniu z funkcji reportFullyDrawn. Zdefiniowane w RFD czasy rozpoczęcia można wyodrębnić za pomocą procesora śledzenia Perfetto, i wysyłanie zdarzenia logu czasu widocznego dla użytkownika.

Korzystanie z śledzenia systemu na urządzeniu

Aby przechwytywać dane systemu, możesz użyć aplikacji na poziomie systemu o nazwie Monitorowanie systemu . Ta aplikacja umożliwia rejestrowanie logów czasu z urządzenia bez musi być podłączone do zasilania czy połączyć z urządzeniem adb.

Korzystanie z Android Studio Memory Profiler

Możesz użyć narzędzia Android Studio Memory Profiler, aby sprawdzić wykorzystanie pamięci. które mogą być spowodowane wyciekami pamięci lub nieprawidłowymi wzorcami użytkowania. Zapewnia transmisję na żywo w widoku przydziałów obiektów.

Aby rozwiązać problemy z pamięcią w aplikacji, zapoznaj się z informacjami z narzędzia Memory Profiler, aby śledzić, dlaczego i jak często występują GC.

Aby przeanalizować profil pamięci aplikacji:

  1. wykrywać problemy z pamięcią;

    Zarejestruj sesję profilowania pamięci ścieżki użytkownika, na której chcesz się skupić. Poszukaj rosnącej liczby obiektów, tak jak na ilustracji 7, która z czasem jak widać na rys. 8.

    alt_text Rysunek 7. Zwiększanie liczby obiektów.

    tekst_alternatywny Rysunek 8. Usuwanie odpadów.

    Po zidentyfikowaniu ścieżki użytkownika, która zwiększa ilość pamięci, przeanalizuj pod kątem głównych przyczyn obniżania wydajności pamięci.

  2. Diagnozuj największe przebarwienia pamięci.

    Wybierz zakres na osi czasu, aby zwizualizować Przydziały i Płytki rozmiar, jak pokazano na ilustracji 9.

    alt_text Rysunek 9. Wartości dla przydziałów i płytkiej wielkości.

    Dane można sortować na kilka sposobów. Oto kilka przykładów jak każdy z nich może pomóc w analizie problemów.

    • Rozmieść według zajęć: przydaje się, gdy chcesz znaleźć zajęcia, które: generują obiekty, które są przechowywane w pamięci podręcznej lub używane ponownie z puli pamięci.

      Jeśli np. aplikacja co sekundę tworzy 2000 obiektów klasy „Vertex”, liczba przydziałów zwiększa się o 2000 co sekundę i widać to podczas sortowania według klasy. Jeśli chcesz ponownie używać tych obiektów, aby uniknąć generowania śmieci, zaimplementuj pulę pamięci.

    • Rozmieść według schematu wywołań: przydaje się, gdy chcesz znaleźć popularne miejsca. ścieżki, w której jest przydzielana pamięć, np. w pętli lub w wykonującą wiele zadań związanych z przydzielaniem.

    • Shallow Size (Mały rozmiar): śledzi tylko pamięć samego obiektu. Jest ona przydatna do śledzenia prostych klas, które składają się głównie z wartości prymitywnych.

    • Przechowywany rozmiar: pokazuje łączną ilość pamięci wiążącej się z obiektem i odwołaniami. do których odwołuje się wyłącznie obiekt. Jest przydatny do śledzenia pamięci ciśnienia spowodowanego przez złożone obiekty. Aby uzyskać tę wartość, zrób całe wspomnienie jak widać na rysunku 10, i przechowywany rozmiar jest dodawany w postaci kolumny, co pokazano na rys. 11.

      alt_text Rysunek 10. Pełny zrzut pamięci.

      Kolumna Zachowany rozmiar.
      Rysunek 11. Zachowany rozmiar.
  3. Pomiar wpływu optymalizacji.

    W przypadku GC jest łatwiej zauważyć i zmierzyć wpływ optymalizacji pamięci. Gdy optymalizacja zmniejsza obciążenie pamięci, widzisz mniej procesów GC.

    Aby zmierzyć wpływ optymalizacji, na osi czasu narzędzia do profilowania należy zmierzyć między GC. Wtedy zauważysz, że czas między GC jest dłuższy.

    Ostatecznie ulepszenia dotyczące pamięci przynoszą następujące korzyści:

    • Wyłączanie aplikacji z powodu braku pamięci prawdopodobnie będzie rzadsze, jeśli aplikacja nie będzie stale odczuwać presji pamięci.
    • Mniejsza liczba zaciemniania pamięci poprawia wskaźniki zaburzeń, zwłaszcza w wersji P99. To jest ponieważ procesory GC mogą powodować rywalizację o CPU, co może prowadzić do tego, że zadania renderowania i te są odroczone w trakcie procesu GC.