Skonfiguruj śledzenie systemu

Możesz skonfigurować śledzenie systemu, aby przechwytywać Profil procesora i wątków aplikacji w krótkim czasie. Potem możesz wykorzystać raport wyjściowy ze logu systemowego, aby poprawić skuteczność reklam.

Konfigurowanie śledzenia systemu opartego na grze

Narzędzie Systrace jest dostępne na dwa sposoby:

Systrace to niewielkie narzędzie, które:

  • Dostarcza dane podstawowe. Systrace przechwytuje dane wyjściowe bezpośrednio z jądra, więc zbierane przez nią dane są niemal identyczne dla połączeń systemowych.
  • Zużywa niewiele zasobów. Systrace wprowadza bardzo niskie koszty urządzenia, zwykle poniżej 1%, ponieważ przesyła dane do bufora w pamięci.

Optymalne ustawienia

Ważne jest, aby narzędzie to przedstawiło rozsądny zestaw argumentów:

  • Kategorie: najlepszy zestaw kategorii dostępnych w systemie opartym na grach. śledzenie to: {sched, freq, idle, am, wm, gfx, view, sync, binder_driver, hal, dalvik}.
  • Rozmiar bufora: zgodnie z ogólną zasadą rozmiar bufora wynosi 10 MB na rdzeń procesora. umożliwia śledzenie o długości około 20 sekund. Jeśli na przykład urządzenie ma dwa procesory czterordzeniowe (łącznie 8 rdzeni), odpowiednią wartość do przekazania Program systrace ma rozmiar 80 000 KB (80 MB).

    Jeśli Twoja gra często zmienia kontekst, zwiększ bufor do 15 MB na rdzeń procesora.

  • Zdarzenia niestandardowe: jeśli zdefiniujesz niestandardowe zdarzenia zdarzeń rejestrowanych w grze, włącz flagę -a, która pozwala firmie Systrace uwzględnić te zdarzenia niestandardowe w raport wyjściowy.

Jeśli używasz wiersza poleceń systrace programu, użyj następującego polecenia do rejestrowania logu czasu systemu, który stosuje sprawdzone metody dotyczące zbioru kategorii, bufora rozmiaru i zdarzeń niestandardowych:

python systrace.py -a com.example.myapp -b 80000 -o my_systrace_report.html \
  sched freq idle am wm gfx view sync binder_driver hal dalvik

Jeśli używasz aplikacji systemowej Systrace na na urządzeniu, wykonaj te czynności, przechwytuje ślad systemu z zastosowaniem sprawdzonych metod dotyczących zbioru kategorii, bufora rozmiaru i zdarzeń niestandardowych:

  1. Włącz opcję Śledź aplikacje z możliwością debugowania.

    Do przy użyciu tego ustawienia, na urządzeniu musi być dostępne 256 lub 512 MB (w zależności od czy procesor ma 4 czy 8 rdzeni), a każdy 64 MB pamięci musi mieć są dostępne jako ciągły fragment.

  2. Wybierz Kategorie i włącz kategorie z tej listy:

    • am: Menedżer aktywności
    • binder_driver: sterownik jądra binarnego
    • dalvik: maszyna wirtualna Dalvik
    • freq: częstotliwość procesora
    • gfx: grafika
    • hal: moduły sprzętowe
    • idle: procesor nieaktywny
    • sched: harmonogram procesora
    • sync: synchronizacja
    • view: wyświetl system
    • wm: menedżer okien
  3. Włącz Śledzenie rekordów.

  4. Wczytaj grę.

  5. Wykonuj interakcje w grze odpowiadające rozgrywce, której do pomiaru wydajności urządzenia.

  6. Gdy tylko zauważysz w grze niepożądane zachowanie, włącz śledzić.

Masz już zebrane statystyki skuteczności które są niezbędne do dalszej analizy problemu.

Aby zaoszczędzić miejsce na dysku, ślady systemu na urządzeniu zapisują pliki w skompresowanym logu czasu format (*.ctrace). Aby zdekompresować ten plik podczas generowania raportu, użyj funkcji wiersza poleceń i dodaj opcję --from-file:

python systrace.py --from-file=/data/local/traces/my_game_trace.ctrace \
  -o my_systrace_report.html

Ulepsz określone obszary skuteczności

W tej sekcji omawiamy kilka częstych problemów z wydajnością w grach mobilnych i opisuje, jak zidentyfikować i ulepszyć te aspekty gry.

Prędkość wczytywania

Gracze chcą jak najszybciej wziąć udział w grze, więc które pozwalają maksymalnie skrócić czas wczytywania gry. Poniżej rozwiązania zwykle pomagają skrócić czas wczytywania:

  • Zastosuj leniwe ładowanie. Jeśli używasz tych samych zasobów w wielu następujących po sobie zasobów: scen lub poziomów w grze, zasoby są wczytywane tylko raz.
  • Zmniejsz rozmiar zasobów. Dzięki temu można połączyć pliki nieskompresowane tych zasobów z pakietem APK gry.
  • Używaj metody kompresji, która oszczędza dysk. Przykładem takiej metody jest zlib.
  • Używaj IL2CPP zamiast mono. (ma zastosowanie tylko wtedy, gdy używasz Unity). IL2CPP zapewnia na wydajność wykonywania skryptów C#.
  • Postaw na wielowątkowość w grze. Więcej informacji znajdziesz w artykule na temat liczby klatek na sekundę spójności.

Spójność liczby klatek

Jednym z najważniejszych elementów w rozgrywce jest osiągnięcie stałą liczbę klatek. Aby łatwiej osiągnąć ten cel, postępuj zgodnie z omówionych w tej sekcji technik optymalizacji.

Wielowątkowość

Przy tworzeniu aplikacji na wiele platform naturalne jest stosowanie wszystkich działań, w ramach gry w jednym wątku. Chociaż ta metoda jest prosta, w wielu silnikach gier, w przypadku Androida nie jest to optymalne urządzenia. W rezultacie gry jednowątkowe często wczytują się powoli i nie mają stałą liczbę klatek.

Układ Systrace przedstawiony na rys. 1 przedstawia typowe zachowanie gry. działające na tylko jednym procesorze naraz:

Schemat nici
w ramach logu czasu systemu

Rysunek 1. Raport Systrace dotyczący gry jednowątkowej
.

Aby poprawić wydajność gry, ustaw ją jako wielowątkową. Zazwyczaj najlepszy model to 2 wątki:

  • Wątek gry, który zawiera główne moduły gry i wysyła informacje o renderowaniu. poleceń.
  • Wątek renderowania, który otrzymuje polecenia renderowania i tłumaczy je na język polecenia graficzne, których GPU urządzenia może używać do wyświetlania sceny.

Interfejs Vulkan API rozszerza ten model, ponieważ może on przekazywać 2 typowe i równoległe bufory. Za pomocą tej funkcji możesz rozłożyć wiele elementów renderowania przez więcej niż 1 procesor, co jeszcze bardziej skraca czas renderowania sceny.

Możesz też wprowadzić zmiany związane z określonym silnikiem, aby poprawić wydajność wielowątkowości:

  • Jeśli opracowujesz grę z wykorzystaniem silnika Unity, włącz Renderowanie wielowątkowe i motywowanie procesora graficznego.
  • Jeśli używasz niestandardowego silnika, upewnij się, że polecenie renderowania potok poleceń i potok poleceń graficznych są prawidłowo dopasowane; W przeciwnym razie może powodować opóźnienia w wyświetlaniu scen z gry.

Po zastosowaniu tych zmian gra powinna zajmować co najmniej 2 procesory jednocześnie, jak widać na rys. 2:

Schemat nici
w ramach logu czasu systemu

Rysunek 2. Raport Systrace dotyczący gry wielowątkowej
.

Wczytuję element interfejsu

Schemat ramki
  stos w ramach zrzutu systemu
Rysunek 3. Raport Systrace dotyczący gry, która renderuje dziesiątki UI elementy w tym samym czasie
.

Podczas tworzenia gry z wieloma funkcjami łatwo jest pokazywać wiele różnych opcji i działań w tym samym czasie. Aby utrzymać stałą liczbę klatek, trzeba jednak wziąć pod uwagę stosunkowo niewielkie rozmiary ekranów mobilnych a interfejs użytkownika powinien być jak najprostszy.

Raport Systrace przedstawiony na Rysunku 3 to przykład ramki interfejsu, próba renderowania zbyt wielu elementów w stosunku do funkcje zabezpieczeń.

Dobrym celem jest skrócenie czasu aktualizacji interfejsu do 2–3 milisekund. Dostępne opcje aby uzyskać tak szybkie aktualizacje, przeprowadzając optymalizacje podobne do poniższych:

  • Aktualizuj tylko te elementy na ekranie, które zostały przeniesione.
  • Ogranicz liczbę tekstur i warstw interfejsu. Rozważ połączenie rozmów graficznych, takich jak cieniowanie i tekstury, które wykorzystują ten sam materiał.
  • Opóźnij operacje animacji elementu do GPU.
  • Wykonuj intensywniejsze usuwanie frustusu i okluzji.
  • W miarę możliwości wykonuj operacje rysowania za pomocą interfejsu Vulkan API. wywołanie rysowania. na Vulkan.

Zużycie energii

Nawet po wprowadzeniu optymalizacji omówionych w poprzedniej sekcji możesz zauważysz, że liczba klatek na sekundę w grze pogarsza się w ciągu pierwszych 45-50 minut gry. Dodatkowo urządzenie może się nagrzewać i zużywać więcej energii baterii.

W wielu przypadkach taki niepożądany zestaw parametrów termicznych i zużycia energii jest związany z rozkładu zadań w grę na procesory urządzenia. Aby zwiększyć zużycie energii przez grę, zastosuj sprawdzone metody opisane na stronie poniższych sekcji.

Utrzymuj wątki zajmujące dużo pamięci na 1 CPU

W wielu urządzeniach mobilnych pamięci podręczne L1 znajdują się w konkretnych procesorach, a pamięci podręczne L2 są w zbiorze procesorów, które mają wspólny zegar. Aby zmaksymalizować trafienia w pamięci podręcznej L1, najlepiej jest zachować główny wątek gry i pozostałe wątki wymagające dużej ilości pamięci i działające na jednym procesorze.

Odrocz pracę krótkotrwałą dla procesorów o mniejszej mocy obliczeniowej

Większość silników gier, w tym Unity, ma informacje o opóźnianiu operacji wątków instancji roboczych na wybierze inne procesory niż w głównym wątku gry. Wyszukiwarka nie jest jednak specyficzna architektura urządzenia i nie jest w stanie przewidzieć, jak najwięcej zadań.

Większość urządzeń z układem SOC ma co najmniej 2 współdzielone zegary, jeden dla szybkich procesorów urządzenia i jeden dla wolnych procesorów. W konsekwencji jest to, że jeśli jeden szybki procesor musi działać z maksymalną prędkością, inne szybkie procesory działają z maksymalną prędkością.

Przykładowy raport na Rys. 4 pokazuje grę wykorzystującą procesory. Jednak tak wysoki poziom aktywności generuje mnóstwo energii i ciepła szybko.

Schemat nici
w ramach logu czasu systemu

Rysunek 4. Raport Systrace pokazujący nieoptymalne przypisanie wątków do procesory urządzenia
.

Aby zmniejszyć ogólne zużycie energii, najlepiej zasugerować algorytmowi szeregowania, krótsza praca, na przykład wczytywanie dźwięku, uruchomione wątki instancji roboczych, wykonywania choreografa – zostanie odroczone do zestawu wolnych procesorów na urządzeniu. Przenieś jak najwięcej pracy na wolniejsze procesory, zachowując jednocześnie wybranej liczby klatek.

Większość urządzeń wymienia wolniejsze procesory przed tymi szybszymi, ale nie można zakładać, że protokół SOC Twojego urządzenia korzysta z tego zamówienia. Aby to sprawdzić, uruchom polecenia podobne do tych widoczne w tym wykrywaniu topologii procesora kod w GitHubie.

Gdy już się dowiesz, które procesory działają wolno na urządzeniu, możesz zadeklarować koligacje z wątkami krótkotrwałymi, które algorytm szeregowania urządzenia co dalej. Aby to zrobić, dodaj w każdym wątku ten kod:

#include <sched.h>
#include <sys/types.h>
#include <unistd.h>

pid_t my_pid; // PID of the process containing your thread.

// Assumes that cpu0, cpu1, cpu2, and cpu3 are the "slow CPUs".
cpu_set_t my_cpu_set;
CPU_ZERO(&my_cpu_set);
CPU_SET(0, &my_cpu_set);
CPU_SET(1, &my_cpu_set);
CPU_SET(2, &my_cpu_set);
CPU_SET(3, &my_cpu_set);
sched_setaffinity(my_pid, sizeof(cpu_set_t), &my_cpu_set);

Naprężenie cieplne

Gdy urządzenia się nagrzeją, mogą spowalniać procesor lub GPU, co może prowadzić i w nieoczekiwany sposób. Gry ze złożoną grafiką, lub długotrwałą aktywność w sieci mogą napotkać problemy.

Używaj interfejsu The The API, aby monitorować zmiany temperatury na urządzeniu i podejmować odpowiednie działania, aby: utrzymywania niższego zużycia energii i niższej temperatury urządzenia. Gdy urządzenie zgłasza stres termiczny, odpocznij bieżące ćwiczenia, aby ograniczyć zużycie energii. Na przykład zmniejsz liczbę klatek na sekundę lub wielkość wielokąta.

Najpierw zadeklaruj obiekt PowerManager zainicjować go w metodzie onCreate(). Dodaj do obiektu detektor informacji o temperaturze.

Kotlin

class MainActivity : AppCompatActivity() {
    lateinit var powerManager: PowerManager

    override fun onCreate(savedInstanceState: Bundle?) {
        powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
        powerManager.addThermalStatusListener(thermalListener)
    }
}

Java

public class MainActivity extends AppCompatActivity {
    PowerManager powerManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
        powerManager.addThermalStatusListener(thermalListener);
    }
}

Określ działania, które mają być wykonywane, gdy detektor wykryje stan . Jeśli Twoja gra używa języka C/C++, dodaj kod do poziomów temperatury ciała w onThermalStatusChanged(), aby wywołać kod natywnej gry za pomocą JNI lub użyj natywnego interfejsu Thermal API.

Kotlin

val thermalListener = object : PowerManager.OnThermalStatusChangedListener() {
    override fun onThermalStatusChanged(status: Int) {
        when (status) {
            PowerManager.THERMAL_STATUS_NONE -> {
                // No thermal status, so no action necessary
            }

            PowerManager.THERMAL_STATUS_LIGHT -> {
                // Add code to handle light thermal increase
            }

            PowerManager.THERMAL_STATUS_MODERATE -> {
                // Add code to handle moderate thermal increase
            }

            PowerManager.THERMAL_STATUS_SEVERE -> {
                // Add code to handle severe thermal increase
            }

            PowerManager.THERMAL_STATUS_CRITICAL -> {
                // Add code to handle critical thermal increase
            }

            PowerManager.THERMAL_STATUS_EMERGENCY -> {
                // Add code to handle emergency thermal increase
            }

            PowerManager.THERMAL_STATUS_SHUTDOWN -> {
                // Add code to handle immediate shutdown
            }
        }
    }
}

Java

PowerManager.OnThermalStatusChangedListener thermalListener =
    new PowerManager.OnThermalStatusChangedListener () {

    @Override
    public void onThermalStatusChanged(int status) {

        switch (status)
        {
            case PowerManager.THERMAL_STATUS_NONE:
                // No thermal status, so no action necessary
                break;

            case PowerManager.THERMAL_STATUS_LIGHT:
                // Add code to handle light thermal increase
                break;

            case PowerManager.THERMAL_STATUS_MODERATE:
                // Add code to handle moderate thermal increase
                break;

            case PowerManager.THERMAL_STATUS_SEVERE:
                // Add code to handle severe thermal increase
                break;

            case PowerManager.THERMAL_STATUS_CRITICAL:
                // Add code to handle critical thermal increase
                break;

            case PowerManager.THERMAL_STATUS_EMERGENCY:
                // Add code to handle emergency thermal increase
                break;

            case PowerManager.THERMAL_STATUS_SHUTDOWN:
                // Add code to handle immediate shutdown
                break;
        }
    }
};

Opóźnienie przy dotknięciu ekranu

gry, które jak najszybciej renderują klatki, tworzą scenariusz powiązany z GPU, w którym bufor ramki staje się przepełniony. Procesor musi poczekać na GPU, który powoduje zauważalne opóźnienie między danymi wpisywanymi przez gracza a danymi wejściowymi. efekt na ekranie.

Aby sprawdzić, czy można poprawić tempo klatek, wykonaj następujące kroki:

  1. Wygeneruj raport Systrace obejmujący kategorie gfx i input. Te kategorie obejmują szczególnie przydatne pomiary, które pozwalają określić czas oczekiwania przy dotknięciu ekranu.
  2. Zapoznaj się z sekcją SurfaceView w raporcie Systrace. przeciążony bufor. powoduje, że liczba oczekujących operacji pobierania z bufora oscyluje między 1 a 2, jak pokazano na ilustracji. na rys. 5.

    Schemat
kolejka bufora w ramach śledzenia systemu

    Rysunek 5. Raport Systrace pokazuje przeciążony bufor, okresowo zapełnia się, aby można było zaakceptować polecenia rysowania
    .

Aby ograniczyć tę niespójność w tempie klatek, wykonaj opisane działania. w następujących sekcjach:

Zintegruj ze swoją grą interfejs API Android Frame Pacing

Interfejs API Android Frame Pacing pomaga wykonywać zamianę klatek i określić interwał, tak aby gra bardziej stabilną liczbę klatek na sekundę.

Zmniejsz rozdzielczość zasobów gry niebędących interfejsem użytkownika

Wyświetlacze na nowoczesnych urządzeniach mobilnych zawierają znacznie więcej pikseli niż odtwarzacz więc można zmniejszyć próbkowanie tak, by 5- czy 10 pikseli zawiera jeden kolor. Ze względu na strukturę większości pamięci podręcznych wyświetlacza najlepiej zmniejsz rozdzielczość tylko w jednym wymiarze.

Nie zmniejsz jednak rozdzielczości elementów interfejsu w grze. To ważne aby zachować grubość linii tych elementów i wystarczająco duże rozmiar docelowego elementu dotykowego dla wszystkich Twoim graczom.

Płynność renderowania

Gdy SurfaceFlinger korzysta z bufora wyświetlacza, by pokazać scenę w grze, aktywność procesora od razu wzrasta. Jeśli występują te skoki aktywności procesora nierównomiernie, może wystąpić zacinanie się w grze. Schemat na rys. 6 wskazuje przyczynę tego zjawiska:

Diagram klatek
brakuje okna Vsync, ponieważ za późno zaczęło się rysować.

Rysunek 6. Raport Systrace pokazujący, jak w klatce może brakować synchronizatora Vsync
.

Jeśli klatka zacznie rysować się zbyt późno (nawet o kilka milisekund), kolejne okno wyświetlania. Ramka musi następnie czekać na (33 milisekundy w przypadku gry przy 30 kl./s), co powoduje zauważalnego opóźnienia z perspektywy gracza.

Aby rozwiązać ten problem, użyj funkcji Android Frame Pacing. API, który zawsze wyświetla nową klatkę w Fala VSync.

Stan pamięci

Jeśli grasz przez dłuższy czas, mogą wystąpić błędy braku pamięci.

W takiej sytuacji sprawdź aktywność procesora w raporcie Systrace i zobacz, jak często system wywołuje demona kswapd. Jeśli jest wiele połączeń podczas wykonywania gry warto przyjrzeć się bliżej temu, zarządza i czyści pamięć.

Więcej informacji znajdziesz w artykule Efektywne zarządzanie pamięcią w grach.

Stan wątku

Podczas poruszania się po typowych elementach raportu Systrace możesz zobaczyć czas, jaki dany wątek spędził w każdym możliwym wątku stanu, wybierając w wątku, co widać na rys. 7:

Schemat
Raport grupy

Rysunek 7. Raport Systrace pokazujący, jak wybór wątku powoduje z podsumowaniem stanu danego wątku
.

Jak widać na Rysunku 7, wątki dotyczące gry nie znajdują się "bieganie" lub „możliwe do uruchomienia” tak często, jak powinno. Poniższa lista wskazuje kilka typowych przyczyn, dla których dany wątek może być okresowo wyświetlany przejście w nietypowy stan:

  • Jeśli wątek jest uśpiony przez dłuższy czas, może to być przyczyną uciążliwości przed rywalizacją o blokadę lub oczekiwaniem na aktywność GPU.
  • Jeśli wątek jest stale blokowany podczas I/O, albo odczytujesz za dużo danych z dysku naraz, lub gra nie jest wydajna.
.

Dodatkowe materiały

Aby dowiedzieć się więcej o poprawianiu wydajności gry, przeczytaj te artykuły dodatkowe materiały:

Filmy