Profilowanie gry WebGL za pomocą flagi about:tracing

Liliana Thompson
Lilli Thompson

Jeśli nie możesz zmierzyć skuteczności, nie da się jej poprawić.

Lord Kelvin

Aby gry HTML5 działały szybciej, musisz najpierw zidentyfikować wąskie gardła wydajności, ale może to być trudne. Pomiar liczby klatek na sekundę to dobry początek, ale aby uzyskać pełny obraz sytuacji, musisz rozumieć niuanse działań w Chrome.

Narzędzie about:tracing dostarcza danych, które pomagają uniknąć pochopnych sposobów obejścia problemów mających na celu poprawę wydajności, choć nie są to zgadywanie. Zaoszczędzisz czas i energię, uzyskasz dokładniejszy obraz działania Chrome w każdej klatce i wykorzystasz te informacje do optymalizacji gry.

Dowiedz się więcej o:tracing

Dostępne w Chrome narzędzie about:tracing daje wgląd we wszystkie działania podejmowane przez Ciebie w Chrome na przestrzeni czasu z tak dużą szczegółowością, że na początku może Cię to przytłaczać. Wiele funkcji Chrome jest gotowych do śledzenia konwersji, dzięki czemu możesz śledzić swoją skuteczność bez żadnej ręcznej obsługi. (Przeczytaj dalszą sekcję na temat ręcznego instrumentowania kodu JS)

Aby wyświetlić widok śledzenia, po prostu wpisz „about:tracing” w omniboksie (pasku adresu) Chrome.

W omniboksie Chrome
Wpisz „about:tracing” w omniboksie Chrome

Korzystając z narzędzia do śledzenia, możesz rozpocząć nagrywanie, uruchomić grę przez kilka sekund, a potem przejrzeć dane z śladu. Dane mogą wyglądać na przykład tak:

Wynik prostego śledzenia
Wynik prostego śledzenia

Tak, to mylące. Porozmawiajmy o tym, jak to czytać.

Każdy wiersz odpowiada profilowanemu procesowi, oś po lewej i prawej stronie wskazuje czas, a każde kolorowe pole to instrumentowane wywołanie funkcji. Istnieją wiersze z różnymi rodzajami zasobów. Do profilowania gier najbardziej interesują funkcje CrGpuMain, które pokazuje działanie procesora graficznego (GPU), i CrRendererMain. Każdy ślad zawiera wiersze CrRendererMain dla każdej otwartej karty w okresie śledzenia (w tym samej karty about:tracing).

Odczytując dane śledzenia, Twoim pierwszym zadaniem jest określenie, który wiersz CrRendererMain odpowiada Twojej grze.

Podświetlono wynik prostego śledzenia
Wyróżniony wynik prostego śledzenia

W tym przykładzie są to: 2216 i 6516. Niestety obecnie nie jest dopracowany sposób na znalezienie aplikacji poza znalezieniem linii, która przeprowadza wiele okresowych aktualizacji (lub jeśli ręcznie dostosowałeś kod za pomocą punktów śledzenia, poszukaj linii zawierającej dane śledzenia). W tym przykładzie wygląda na to, że 6516 wykonuje pętlę główną na podstawie częstotliwości aktualizacji. Jeśli zamkniesz wszystkie inne karty przed rozpoczęciem śledzenia, łatwiej będzie znaleźć prawidłową konfigurację CrRendererMain. Jednak nadal mogą się pojawiać wiersze CrRendererMain dla procesów innych niż Twoja gra.

Szukam ramki

Po znalezieniu odpowiedniego wiersza w narzędziu do śledzenia gry kolejnym krokiem jest znalezienie głównej pętli. Główna pętla w danych śledzenia wygląda jak powtarzający się wzorzec. Możesz poruszać się po danych śledzenia za pomocą klawiszy W, A, S i D: A i D służą do poruszania się w lewo lub w prawo (do przodu i do tyłu w czasie) oraz W i S, aby powiększać i pomniejszać dane. Jeśli gra działa z częstotliwością 60 Hz, pętla główna powinna być wzorcem powtarzanym co 16 milisekund.

Wygląda jak 3 ramki wykonania
Wygląda na 3 ramki wykonania

Gdy poznasz już klimat gry, możesz dowiedzieć się, co dokładnie robi kod w każdej klatce. Używaj W, A, S i D, aby powiększać obraz do momentu, aż tekst w polach funkcji będzie dostępny.

Głęboko w ramce wykonywania
Wejdź w ramkę wykonywania

Ten zestaw pól zawiera serię wywołań funkcji, przy czym każde wywołanie jest reprezentowane przez kolorowe pole. Każda funkcja została wywołana przez pole nad nią, więc w tym przypadku można zobaczyć obiekt MessageLoop::RunTask o nazwie RenderWidget::OnReplaceBuffersComplete, który z kolei o nazwie RenderWidget::DoDeferredUpdate będzie się nazywał itd. Dzięki tym danym możesz uzyskać pełny obraz tego, co i jak długo trwało każde wykonanie.

Tu chyba coś się klei. Informacje udostępniane przez parametr about:tracing to nieprzetworzone wywołania funkcji z kodu źródłowego Chrome. Na podstawie nazwy można wywnioskować, jak działa dana funkcja, ale informacje te nie są łatwe dla użytkownika. Dobrze jest zobaczyć ogólny przebieg kadru, ale aby zrozumieć, co się dzieje, potrzebujesz czegoś bardziej czytelnego dla człowieka.

Dodawanie tagów śledzenia

Na szczęście istnieje łatwy sposób dodania ręcznej instrumentacji do kodu, który pozwala na tworzenie danych śledzenia: console.time i console.timeEnd.

console.time("update");
update();
console.timeEnd("update");
console.time("render");
update();
console.timeEnd("render");

Powyższy kod tworzy w nazwie widoku śledzenia nowe pola z określonymi tagami. Jeśli więc ponownie uruchomisz aplikację, zobaczysz pola „update” i „render” (aktualizacja) i „render” (aktualizacja), które pokazują czas, jaki upłynął między wywołaniem początkowym a końcowym każdego tagu.

Tagi dodane ręcznie
Tagi dodane ręcznie

Korzystając z tego rozwiązania, możesz tworzyć czytelne dla człowieka dane śledzenia, aby śledzić w kodzie obszary interaktywne.

GPU czy CPU?

W przypadku grafiki z akceleracją sprzętową jedno z najważniejszych pytań, jakie można zadać podczas profilowania, to: czy ten kod jest powiązany z procesorem GPU, czy z procesorem? Każda klatka wiąże się z renderowaniem grafiki na GPU i pewnymi elementami logicznymi na procesorach. Aby dowiedzieć się, co spowalnia grę, musisz sprawdzić, jak zrównoważona praca obu stron jest równoważona z obu tych zasobów.

Najpierw znajdź w widoku śledzenia linię CrGPUMain, która wskazuje, czy GPU jest zajęty w określonym czasie.

Ślady GPU i CPU

Jak widać, każda klatka gry powoduje, że procesory działają zarówno w CrRendererMain, jak i w GPU. Powyższy ślad pokazuje bardzo prosty przypadek użycia, w którym procesor i GPU są nieaktywne przez większość każdej 16 ms klatki.

Widok śledzenia jest bardzo przydatny, gdy masz grę, która działa wolno i nie masz pewności, które zasoby wykorzystujesz w pełni. Kluczem do debugowania jest zapoznanie się z relacjami między układami GPU i CPU. Weźmy ten sam przykład co poprzednio, dodając do niego trochę pracy w pętli aktualizacji.

console.time("update");
doExtraWork();
update(Math.min(50, now - time));
console.timeEnd("update");

console.time("render");
render();
console.timeEnd("render");

Zobaczysz ślad podobny do tego:

Ślady GPU i CPU

Co wynika z tego śladu? Widać, że czas trwania klatki na obrazie wynosi od 2270 ms do 2320 ms, co oznacza, że każda klatka zajmuje około 50 ms (przy 20 Hz). Obok pola aktualizacji widoczne są kolorowe pola reprezentujące funkcję renderowania, ale cała ramka jest całkowicie zdominowana przez aktualizację.

W przeciwieństwie do tego, co dzieje się na procesorze, widać, że przez większość czasu jest on nieaktywny. Aby zoptymalizować ten kod, możesz znaleźć operacje, które można wykonać w kodzie programu do cieniowania, i przenieść je do GPU. W ten sposób maksymalnie wykorzystasz zasoby.

Co się stanie, gdy kod cieniowania działa wolno, a GPU jest przeciążony? A co, jeśli usuniemy niepotrzebną pracę z CPU i dodamy więcej pracy do kodu programu do cieniowania fragmentów. Oto niepotrzebnie kosztowny program do cieniowania fragmentów:

#ifdef GL_ES
precision highp float;
#endif
void main(void) {
  for(int i=0; i<9999; i++) {
    gl_FragColor = vec4(1.0, 0, 0, 1.0);
  }
}

Jak wygląda ślad kodu za pomocą tego narzędzia do cieniowania?

Ślady GPU i CPU w przypadku korzystania z wolnego kodu GPU
Śledzenie procesora i GPU w przypadku korzystania z wolnego kodu GPU

Ponownie zwróć uwagę na czas trwania klatki. Tutaj powtarzający się wzorzec zmienia się z 2750 ms na 2950 ms, co trwa 200 ms (z częstotliwością około 5 Hz). Wiersz CrRendererMain jest prawie całkowicie pusty, co oznacza, że procesor jest przez większość czasu nieaktywny, a GPU jest przeciążony. To pewny znak, że cieniowanie jest za ciężkie.

Jeśli nie wiesz dokładnie, co powoduje małą liczbę klatek na sekundę, możesz zauważyć aktualizację 5 Hz. Może Cię to kusić, aby zagłębić się w kod gry i zacząć optymalizować lub usuwać logikę gry. W tym przypadku nie byłoby to nic z tego, bo logika w pętli gry nie polega na pochłanianiu czasu. W rzeczywistości ten ślad wskazuje, że zwiększenie mocy obliczeniowej procesora w przypadku każdej klatki byłoby w zasadzie „wolne”, ponieważ procesor pozostaje bezczynny, a zwiększenie ilości pracy nie wpływa na czas renderowania klatki.

Prawdziwe przykłady

Spójrzmy teraz, jak wygląda śledzenie danych z prawdziwej gry. Jedną z zalet gier opartych na otwartych technologiach internetowych jest możliwość sprawdzania, co dzieje się w Twoich ulubionych usługach. Jeśli chcesz przetestować narzędzia do profilowania, wybierz ulubiony tytuł WebGL ze sklepu Chrome Web Store i profiluj go za pomocą właściwości about:tracing. To przykładowy zrzut ekranu ze znakomitej gry WebGL Skid Racer.

Śledzenie prawdziwego meczu
Śledzenie prawdziwej gry

Wygląda na to, że każda klatka trwa około 20 ms, co oznacza, że liczba klatek na sekundę wynosi około 50 kl./s. Widać, że nakład pracy jest równoważny między procesorem a układem GPU, ale największe zapotrzebowanie jest na GPU. Jeśli chcesz zobaczyć, jak wygląda profilowanie prawdziwych przykładów gier WebGL, spróbuj pobawić się niektórymi tytułami w Chrome Web Store stworzonymi z użyciem WebGL, w tym:

Podsumowanie

Jeśli chcesz, aby gra działała z częstotliwością 60 Hz, na każdą klatkę wszystkie operacje muszą być zmieścić się w 16 ms procesora i 16 ms na GPU. Masz 2 zasoby, których można używać równolegle. Możesz też przełączać się między nimi w celu zmaksymalizowania wydajności. Widok ten w Chrome to:nieocenione narzędzie, które pozwala sprawdzić, co tak naprawdę robi Twój kod, i pomaga zmaksymalizować czas programowania przez rozwiązanie odpowiednich problemów.

Co dalej?

Oprócz GPU możesz też śledzić inne części środowiska wykonawczego Chrome. Chrome Canary, wczesna wersja Chrome, jest przystosowana do śledzenia operacji wejścia-wyjścia, IndexedDB i kilku innych działań. Aby dowiedzieć się więcej o bieżącym stanie śledzenia zdarzeń, przeczytaj ten artykuł w Chromium.

Jeśli tworzysz gry internetowe, obejrzyj poniższy film. Oto prezentacja zespołu Google Game Developer Advocate na GDC 2012 o optymalizacji wydajności gier w Chrome: