Przeprowadź analizę z użyciem renderowania GPU profilu

Narzędzie Profil renderowania GPU wskazuje czas, jaki zajmuje renderowanie poprzedniej klatki w ramach poszczególnych etapów procesu renderowania. Wiedza ta pomoże Ci zidentyfikować wąskie gardła w procesie, co pozwoli zoptymalizować wydajność renderowania aplikacji.

Na tej stronie omawiamy pokrótce, co dzieje się na każdym etapie potoku, oraz omawiamy problemy, które mogą powodować wąskie gardła na tym etapie. Zanim zaczniesz czytać tę stronę, zapoznaj się z informacjami przedstawionymi w artykule Profilowanie renderowania GPU. Aby zrozumieć, jak wszystkie etapy się ze sobą łączą, warto zapoznać się z tym, jak działa ścieżka renderowania.

Prezentacja wizualna

Narzędzie Profile GPU Rendering wyświetla etapy i ich względne czasy w postaci wykresu: kolorowego histogramu. Rysunek 1 przedstawia przykład takiego wyświetlacza.

Rysunek 1. Profil renderowania GPU

Każdy segment każdego pionowego słupka widocznego na wykresie Renderowanie w GPU profilu przedstawia etap potoku i jest podświetlany określonym kolorem na wykresie słupkowym. Rysunek 2 przedstawia znaczenie każdego wyświetlanego koloru.

Rysunek 2. Legenda wykresu renderowania GPU profilu

Gdy już poznasz znaczenie poszczególnych kolorów, możesz kierować reklamy na określone aspekty aplikacji, aby zoptymalizować jej renderowanie.

Etapy i ich znaczenie

W tej sekcji wyjaśniamy, co dzieje się na każdym etapie odpowiadającemu kolorowi z Rys. 2, a także wskazuje wąskie gardła, na które należy uważać.

Obsługa danych wejściowych

Etap przetwarzania danych wejściowych w potoku mierzy, ile czasu aplikacja poświęciła na obsługę zdarzeń wprowadzania danych. Te dane wskazują, ile czasu aplikacja spędziła na wykonywaniu kodu wywołanego w wyniku wywołania zwrotnego zdarzenia wejściowego.

Gdy segment jest duży

Wysokie wartości w tym zakresie są zwykle wynikiem zbyt dużej lub zbyt złożonej pracy wykonywanej w ramach wywołań zwrotnych zdarzeń w obsługach obsługi wejścia. Ponieważ te funkcje zwracające wywołania są zawsze wykonywane w głównym wątku, rozwiązania tego problemu koncentrują się na optymalizacji pracy bezpośrednio lub przekierowaniu jej na inny wątek.

Warto też pamiętać, że na tym etapie może pojawić sięRecyclerView przewijanie. RecyclerView przewija ekran od razu po przetworzeniu zdarzenia dotknięcia. W efekcie może ona zawyżać liczbę wyświetleń nowych produktów lub wypełniać dane o takich wyświetleniach. Dlatego ważne jest, aby ta operacja przebiegła jak najszybciej. Narzędzia do profilowania, takie jak Traceview czy Systrace, mogą pomóc w dokładniejszym zbadaniu problemu.

Animacja

Faza Animacje pokazuje, ile czasu zajęło przeprowadzenie oceny wszystkich animacji działających w danej klatce. Najczęściej używane są animacje ObjectAnimator, ViewPropertyAnimator i Przejścia.

Gdy segment jest duży

Wysokie wartości w tym obszarze są zwykle wynikiem prac wykonywanych w związku ze zmianą właściwości animacji. Na przykład animacja przerzucania, która powoduje przewijanie ListView lub RecyclerView, powoduje dużą inflację i liczbę wyświetleń.

Pomiar/układ

Aby Android mógł rysować elementy widoku na ekranie, wykonuje 2 konkretne operacje w różnych układach i widokach hierarchii widoków.

Najpierw system mierzy wyświetlenia. Każdy widok i rozkład ma określone dane opisujące rozmiar obiektu na ekranie. Niektóre widoki mogą mieć określony rozmiar, inne – dostosowują się do rozmiaru kontenera układu nadrzędnego.

Po drugie, system rozmieszcza elementy widoku. Gdy system obliczy rozmiary widoków podrzędnych, może wybrać układ, rozmiar i pozycję widoków na ekranie.

System wykonuje pomiary i ustawia układ nie tylko w przypadku widoków, które mają być wyświetlane, ale także w przypadku ich nadrzędnych hierarchii aż do widoku wyższego poziomu.

Gdy ten segment jest duży

Jeśli Twoja aplikacja spędza dużo czasu na każdym klatku w tym obszarze, zwykle jest to spowodowane dużą liczbą widoków, które trzeba rozmieścić, lub problemami takimi jak podwójne naliczanie opłat w niewłaściwym miejscu w hierarchii. W każdym z tych przypadków adresowanie wydajności obejmuje poprawę wydajności hierarchii widoków.

Kod dodany do pliku onLayout(boolean, int, int, int, int) lub onMeasure(int, int) może też powodować problemy ze skutecznością. Traceview i Systrace pomogą Ci zbadać stosy wywołań pod kątem wykrywania problemów z kodem.

Rysowanie

Etap rysowania przekształca operacje renderowania widoku, takie jak rysowanie tła czy tekstu, w sekwencję poleceń do rysowania. System rejestruje te polecenia w wyświetlanej liście.

Pasek Rysuj rejestruje czas potrzebny na przechwycenie poleceń na listę wyświetlania w przypadku wszystkich widoków, które wymagają zaktualizowania na ekranie w danej klatce. Zmierzony czas dotyczy dowolnego kodu dodanego do obiektów UI w aplikacji. Przykładami takiego kodu są klasy onDraw(), dispatchDraw() i różne klasy draw ()methods należące do podklas klasy Drawable.

Gdy segment jest duży

Uproszczone rozumienie tych danych polega na tym, że wskazują one, ile czasu zajęło wykonanie wszystkich wywołań funkcji onDraw() w przypadku każdego unieważnionego widoku. Ten pomiar uwzględnia czas spędzony na wysyłaniu dzieciom poleceń rysowania oraz elementów rysunkowych, które mogą być dostępne. Dlatego, gdy widzisz taki skok, przyczyną może być nagłe unieważnienie wielu wyświetleń. Uniemożliwia to odtworzenie list wyświetlanych widoków. Długi czas przetwarzania może być też spowodowany tym, że masz kilka widoków niestandardowych z bardzo złożoną logiką w metodach onDraw().

Synchronizacja/przesyłanie

Dane synchronizacji i przesyłania odpowiadają czasowi przesyłania obiektów bitmapy z pamięci procesora do pamięci GPU w bieżącej klatce.

Procesor i GPU, podobnie jak różne procesory, mają różne obszary pamięci RAM przeznaczone do przetwarzania. Gdy rysujesz bitmapę na Androidzie, system przenosi ją do pamięci GPU, zanim GPU wyrenderuje ją na ekranie. Następnie GPU umieszcza bitmapę w pamięci podręcznej, aby system nie musiał ponownie przesyłać danych, chyba że tekstura zostanie usunięta z pamięci podręcznej GPU.

Uwaga: na urządzeniach z Lollipop ten etap jest fioletowy.

Gdy segment jest duży

Wszystkie zasoby ramki muszą znajdować się w pamięci GPU, by można było ich użyć do renderowania klatki. Oznacza to, że wysoka wartość tego rodzaju danych może oznaczać dużą liczbę małych zasobów lub małą liczbę bardzo dużych zasobów. Typowym przypadkiem jest wyświetlanie przez aplikację pojedynczej bitmapy o rozmiarze zbliżonym do rozmiaru ekranu. Inny przypadek to wyświetlanie dużej liczby miniatur w aplikacji.

Aby zmniejszyć ten pasek, możesz zastosować takie techniki jak:

  • Upewnij się, że rozdzielczość bitmapy nie jest znacznie większa niż rozmiar, w którym będą wyświetlane. Aplikacja nie powinna na przykład wyświetlać obrazu o wymiarach 1024 x 1024 jako obrazu o wymiarach 48 x 48.
  • Wykorzystanie prepareToDraw() do asynchronicznego przesyłania bitmapy przed następnym etapem synchronizacji.

Wydawanie poleceń

Segment Wydawanie poleceń przedstawia czas potrzebny na wydanie wszystkich poleceń niezbędnych do wyświetlenia list wyświetlania na ekranie.

Aby system rysował listy wyświetlaczy na ekranie, wysyła niezbędne polecenia do GPU. Zwykle wykonuje to działanie za pomocą interfejsu OpenGL ES API.

Ten proces zajmuje trochę czasu, ponieważ system wykonuje ostateczną transformację i przycinanie dla każdego polecenia przed wysłaniem go do procesora graficznego. Po stronie GPU, która oblicza ostatnie polecenia, staje się dodatkowy narzut. Te polecenia obejmują ostateczne przekształcenia i dodatkowe przycinanie.

Gdy ten segment jest duży

Czas spędzony na tym etapie jest bezpośrednim miernikiem złożoności i liczby list wyświetlania, które system renderuje w danym interwale czasu. Na przykład wykonywanie wielu operacji rysowania, zwłaszcza w przypadkach, gdy z nieruchomości niesie się niewielki koszt każdego elementu podstawowego, może ten czas ulec zawyżeniu. Na przykład:

Kotlin

for (i in 0 until 1000) {
    canvas.drawPoint()
}

Java

for (int i = 0; i < 1000; i++) {
    canvas.drawPoint()
}

jest dużo droższa niż:

Kotlin

canvas.drawPoints(thousandPointArray)

Java

canvas.drawPoints(thousandPointArray);

Wykonywanie poleceń nie zawsze ma ścisłą korelację z rzeczywistym rysowaniem list wyświetlanych. W przeciwieństwie do polecenia problemu, który rejestruje czas potrzebny na wysłanie poleceń rysowania do GPU, wskaźnik Draw reprezentuje czas potrzebny na przechwycenie wydanych poleceń na listę wyświetlania.

Ta różnica wynika z tego, że system w miarę możliwości przechowuje w pamięci podręcznej listy wyświetleń. W wyniku tego zdarzają się sytuacje, w których przewijanie, przekształcanie lub animacja wymagają ponownego wysłania listy wyświetlania, ale nie trzeba jej odtwarzać – czyli od nowa tworzyć poleceń rysowania – od podstaw. W związku z tym pasek „Polecenia dotyczące problemu” może być wysoki, ale nie będzie widać wysokiego paska Polecenia rysowania.

Przetwarzanie/wymiana buforów

Gdy Android zakończy przesyłanie całej listy wyświetlaczy do GPU, system wydaje ostatnie polecenie, które informuje sterownik karty graficznej, że zakończył działanie z bieżącą klatką. W tym momencie sterownik może w końcu wyświetlić zaktualizowany obraz na ekranie.

Gdy ten segment jest duży

Pamiętaj, że GPU wykonuje zadania równolegle z procesorem. System Android generuje polecenia rysowania do GPU, a następnie przechodzi do następnego zadania. GPU odczytuje te polecenia rysowania z kolejki i je przetwarza.

W sytuacjach, gdy procesor wysyła polecenia szybciej niż GPU, kolejka komunikacji między takimi procesorami może zostać zapełniona. W takim przypadku procesor blokuje się i czeka, aż w kole pojawi się miejsce na kolejne polecenie. Stan pełnej kolejki występuje często na etapie Zmiana buforów, ponieważ w tym momencie została przesłana cała klatka poleceń.

Kluczem do rozwiązania tego problemu jest zmniejszenie złożoności pracy wykonywanej na GPU w sposób podobny do tego, który stosujesz w etapie „Wydawanie poleceń”.

Różne

Oprócz czasu, którego system renderowania potrzebuje na wykonanie swoich zadań, w wątku głównym jest realizowany dodatkowy zbiór zadań, który nie ma nic wspólnego z renderowaniem. Czas potrzebny na wykonanie tej pracy jest raportowany jako czas inny. Zwykle oznacza pracę, która może mieć miejsce w wątku interfejsu między 2 kolejnymi klatkami renderowania.

Gdy ten segment jest duży

Jeśli ta wartość jest wysoka, prawdopodobnie aplikacja ma wywołania zwrotne, intencje lub inne zadania, które powinny być wykonywane w innym wątku. Narzędzia takie jak śledzenie metod lub Systrace zapewniają wgląd w zadania uruchomione w wątku głównym. Te informacje pomogą Ci zwiększyć skuteczność reklam.