Profiling w Angular

Myślę, że niemalże każdy programist(k)a aplikacji frontendowych zadaje sobie nie raz pytanie – co ten widok tak kur** zamula!? I oczywiście zaraz w głowie rodzi się drugie pytanie, co powoduje to zamulenie?! gdzie leży błąd?

Świadomy developer Angulara, aby móc odpowiedzieć sobie na takie pytania, zdecydowanie musi umieć korzystać z narzędzi do profilingu (poprzez profiling rozumiemy analizę programu, np. poprzez zmierzenie wydajności).

W tym artykule pokażę Ci, jak korzystać z wbudowanych narzędzi do profilingu w Angularze, w celu pomiaru wydajności cyklu detekcji.

NgProfiler

Aby rozpocząć pomiary cyklu detekcji, musimy najpierw się upewnić, czy w pliku main.ts, mamy odblokowane debugTools:

Jeśli tak, to możemy już śmiało odpalić naszą aplikację i przejść do narzędzi deweloperskich. Kolejno przechodzimy do konsoli, do obojętnie jakiego widoku aplikacji i wpisujemy:

Uruchomienie metody timeChangeDetection() spowoduje odpalenie systemu detekcji w pętli a kolejno wyświetlenie pomiarów:

  • ile milisekund zajmuje średnio pojedyncza runda systemu detekcji dla aktualnego stanu widoku czyli msPerTick (im mniej tym lepiej, oczywiste)
  • ile w czasie sprawdzenia poszło ticków  czyli numTicks (im więcej tym lepiej! dla pustego widoku, może być w okolicach 40 tys. ++)

Mówiąc o „tickach„, mam na myśli odpalenie metody this.appRef.tick(), która uruchamia jawnie system detekcji wraz z jego wszystkimi side-effects. Więc im więcej razy udało nam się uruchomić system detekcji w czasie pomiarów, tym lepiej (krótszy czas per tick – szybsze sprawdzenie).

Jak to działa? metoda uruchamia minimum 5 rund detekcji i dla minimum 500ms. Nie symuluje żadnych kliknięć usera ani żadnych innych akcji, sprawdza po prostu widok tak jak go widzisz w danym momencie.

Proponuję pochodzić po różnych widokach aplikacji i zobaczyć jak różnią się wyniki.

Testujemy profiler

Dobra, wiemy już, że możemy sprawdzić ile średnio trwa jedna runda systemu detekcji na zadanym widoku. To jak możemy sprawdzić, co zamula naszą aplikację?. Najpewniej, jakieś ciężkie funkcje, które uruchamiają się z każdym cyklem detekcji i zanim Angular je przetrawi, musi minąć trochę czasu.

Stwórzmy przypadek testowy 😉
Templatka:

Klasa komponentu:

Puszczamy sobie pętlę, która zwraca nam wartość „i”. Wartość jest uzależniona od ilości iteracji w pętli. Podczas uruchomienia systemu detekcji, Angular odświeży bindingi na widoku (wąsy {{ }}) i zawoła naszą funkcję loop.

Wyniki pomiarów dla:

  • x < 100: {msPerTick: 0.021067486730337422, numTicks: 23738}
  • x < 100000 {msPerTick: 0.3351640991225018, numTicks: 1493}
  • x < 100000000 {msPerTick: 261.8400000035763, numTicks: 5}

Jak widać dla 100mln iteracji, czas jednej rundy detekcji trwał średnio 261ms na jednego ticka!

Dobrze, jeśli ten czas nie przekraczałby 15ms.

Record: true

Dobra… w naszym przykładzie dobrze wiemy, że za zamulenie widoku odpowiada funkcja loop. Jak odnaleźć się w dużych, złożonych widokach?

Możemy do naszej metody dodać dodatkowy config, który spowoduje, że akcje zostaną nagrane w CPU Profiler:

Po dodaniu {record: true} przechodzimy do zakładki JavaScript Profiler w Chrome DevTools:

Zawołania metody timeChangeDetection z record: true zostają rejestrowane w zakładce changeDetection (po lewej), jako kolejne rundy:

 

Spójrzmy na ostatnią rundę, gdzie było 100mln iteracji, pokazuje nam dokładnie, że odpowiada za to funkcja LoginComponent.loop. Super, właśnie tego oczekiwaliśmy!

Jeśli zastanawiasz się czym różni się zakładka self time od total time, to śpieszę z wyjaśnieniem:

  • self time – czas egzekucji samej funkcji
  • total time – czas egzekucji samej funkcji wraz ze wszystkimi funkcjami, które są w niej wywołane.

Podsumowanie

Korzystanie z profilera i metody timeChangeDetection z nagraniem wyników do profilera może być bardzo pomocne w celu poprawy wydajności aplikacji, zwłaszcza, jeśli nie wiemy, jakie metody odpowiadają za spowolnienie aplikacji. Dzięki zastosowaniu profilingu, jesteśmy w stanie je odnaleźć.

One Comment

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *