Jak i dlaczego opracowaliśmy statystyki skuteczności

W Chrome 102 w Narzędziach deweloperskich znajdziesz nowy eksperymentalny panel Statystyki wydajności. W tym poście omówimy nie tylko powody, dla których pracowaliśmy nad nowym panelem, ale też wyzwania techniczne, z którymi musieliśmy się zmierzyć, oraz decyzje, które podjęliśmy po drodze.

ALT_TEXT_HERE

Dlaczego warto utworzyć kolejny panel?

(Jeśli jeszcze go nie widzieliście, opublikowaliśmy film o tym, dlaczego warto korzystać z panelu Statystyki wydajności i jak dzięki niemu uzyskać przydatne informacje o wydajności witryny).

Dotychczasowy panel wydajności to świetne narzędzie, jeśli chcesz wyświetlać wszystkie dane o swojej witrynie w jednym miejscu, ale uznaliśmy, że może być nieco przytłaczający. Jeśli nie jesteś ekspertem w zakresie wydajności, trudno Ci będzie dokładnie określić, na co zwrócić uwagę i które części nagrania są istotne.

Otwórz panel Statystyki, w którym możesz wyświetlić oś czasu śledzenia i sprawdzić dane, a także uzyskać przydatną listę głównych „statystyk”, które warto dokładniej przeanalizować. Statystyki pomogą Ci zidentyfikować problemy, takie jak żądania blokujące renderowanie, zmiany układu i długie zadania, które mogą negatywnie wpływać na wydajność wczytywania stron w Twojej witrynie, a w szczególności na wyniki podstawowych wskaźników internetowych. Oprócz oznaczania problemów statystyki wydajności będą zawierać praktyczne sugestie dotyczące poprawy wyników CWV oraz linki do dodatkowych materiałów i dokumentacji.

Link do opinii w panelu

Ten panel jest w wersji eksperymentalnej i chętnie poznamy Twoją opinię. Jeśli napotkasz jakieś błędy lub masz prośby dotyczące funkcji, które Twoim zdaniem pomogą Ci w pracy nad wydajnością witryny, daj nam znać.

Jak powstały statystyki skuteczności

Podobnie jak resztę Narzędzi deweloperskich, panel Statystyki wydajności stworzyliśmy w TypeScript i użyliśmy komponentów internetowych opartych na lit-html do zbudowania interfejsu. Narzędzie Performance Insights różni się tym, że główny interfejs użytkownika to element HTML canvas, a oś czasu jest rysowana na tym obszarze. Wiele zawiłości wynika z zarządzania tym obszarem roboczym: nie tylko rysowania odpowiednich szczegółów we właściwym miejscu, ale też zarządzania zdarzeniami myszy (np. gdzie użytkownik kliknął obszar roboczy? Czy kliknęli zdarzenie, które narysowaliśmy?) i upewnić się, że skutecznie ponownie renderujemy obszar rysowania.

Wiele ścieżek na jednym obszarze roboczym

W przypadku danej witryny chcemy renderować wiele „ścieżek”, z których każda reprezentuje inną kategorię danych. Na przykład panel Statystyki domyślnie wyświetla 3 ścieżki:

W miarę dodawania kolejnych funkcji do panelu spodziewamy się, że będzie się w nim pojawiać więcej utworów.

Początkowo myśleliśmy, że każdy z tych ścieżek będzie renderować własny element <canvas>, dzięki czemu główny widok będzie składać się z wielu elementów canvas ułożonych pionowo. Uprościłoby to renderowanie na poziomie ścieżki, ponieważ każda ścieżka mogłaby być renderowana oddzielnie i nie byłoby ryzyka renderowania ścieżki poza jej granicami. Niestety to podejście ma 2 główne problemy:

Elementy canvas są kosztowne w (ponownym) renderowaniu. Używanie wielu elementów canvas jest droższe niż używanie jednego, nawet jeśli jest on większy. Renderowanie nakładek, które obejmują wiele ścieżek (np. pionowych linii oznaczających zdarzenia, takie jak czas FCP), staje się skomplikowane: musimy renderować na wielu obszarach roboczych i zapewnić, że wszystkie są renderowane razem i prawidłowo wyrównane.

Użycie jednego elementu canvas w całym interfejsie wymagało od nas znalezienia sposobu na zapewnienie, że każda ścieżka będzie renderowana we właściwych współrzędnych i nie będzie się nakładać na inną ścieżkę. Jeśli na przykład ścieżka ma wysokość 100 pikseli, nie możemy pozwolić na renderowanie na niej elementu o wysokości 120 pikseli, który będzie nachodzić na ścieżkę znajdującą się poniżej. Aby rozwiązać ten problem, możemy użyć clip. Przed wyrenderowaniem każdej ścieżki rysujemy prostokąt reprezentujący widoczne okno ścieżki. Dzięki temu wszystkie ścieżki narysowane poza tymi granicami zostaną przycięte przez obszar rysowania.

canvasContext.beginPath();
canvasContext.rect(
    trackVisibleWindow.x, trackVisibleWindow.y, trackVisibleWindow.width, trackVisibleWindow.height);
canvasContext.clip();

Nie chcieliśmy też, aby każdy ślad musiał znać swoją pozycję w pionie: każdy ślad powinien renderować się tak, jakby był renderowany w punkcie (0, 0), a my mamy komponent wyższego poziomu (nazywamy go TrackManager), który zarządza ogólną pozycją śladu. Możesz to zrobić za pomocą funkcji translate, która przesuwa obszar rysowania o określoną pozycję (x, y). Na przykład:

canvasContext.translate(0, 10); // Translate by 10px in the y direction
canvasContext.rect(0, 0, 10, 10); // draw a rectangle at (0, 0) that’s 10px high and wide

Pomimo tego, że rect ustawia pozycję 0, 0, zastosowane tłumaczenie spowoduje, że prostokąt zostanie wyrenderowany w pozycji 0, 10. Dzięki temu możemy pracować na podstawie ścieżki, tak jakbyśmy renderowali w punkcie (0, 0), a menedżer ścieżek tłumaczy podczas renderowania każdej ścieżki, aby zapewnić prawidłowe renderowanie każdej ścieżki poniżej poprzedniej.

Płótna poza ekranem dla ścieżek i najciekawszych momentów

Renderowanie elementu canvas jest stosunkowo kosztowne, dlatego chcemy, aby panel Statystyki działał płynnie i szybko podczas Twojej pracy. Czasami nie da się uniknąć ponownego renderowania całego obszaru rysowania, np. gdy zmienisz poziom powiększenia, musimy zacząć od nowa i ponownie wyrenderować wszystko. Ponowne renderowanie elementu Canvas jest szczególnie kosztowne, ponieważ nie można po prostu ponownie wyrenderować jego małej części. Trzeba wyczyścić cały element i narysować go od nowa. W przeciwieństwie do ponownego renderowania DOM, w przypadku którego narzędzia mogą obliczyć minimalną ilość pracy i nie usuwać wszystkiego, aby zacząć od nowa.

Jednym z obszarów, w którym napotkaliśmy problemy wizualne, było wyróżnianie. Gdy najedziesz kursorem na rodzaj danych w panelu, wyróżnimy go na osi czasu. Podobnie, gdy najedziesz kursorem na statystykę dotyczącą danego zdarzenia, narysujemy wokół niego niebieską ramkę.

Ta funkcja została po raz pierwszy zaimplementowana przez wykrywanie ruchu myszy nad elementem, który wywołuje wyróżnienie, a następnie rysowanie tego wyróżnienia bezpośrednio na głównym obszarze roboczym. Problem pojawia się, gdy musimy usunąć wyróżnienie: jedyną opcją jest ponowne narysowanie wszystkiego. Nie można po prostu ponownie narysować obszaru, w którym znajdowało się wyróżnienie (bez wprowadzania ogromnych zmian w architekturze), ale ponowne rysowanie całego obszaru tylko dlatego, że chcemy usunąć niebieską obwódkę wokół jednego elementu, wydawało się przesadą. Występowało też opóźnienie wizualne, jeśli szybko przesuwałeś mysz nad różnymi elementami, aby wywołać wiele wyróżnień w szybkiej kolejności.

Aby to naprawić, podzieliliśmy interfejs na 2 obszary robocze poza ekranem: „główny” obszar roboczy, na którym renderowane są ścieżki, oraz obszar roboczy „najważniejszych momentów”, na którym rysowane są najważniejsze momenty. Następnie renderujemy, kopiując te obszary na pojedynczy obszar widoczny na ekranie użytkownika. W kontekście elementu canvas możemy użyć metody drawImage, która może przyjmować inny element canvas jako źródło.

Dzięki temu usunięcie wyróżnienia nie powoduje ponownego rysowania głównego obszaru roboczego. Zamiast tego możemy wyczyścić obszar roboczy na ekranie, a następnie skopiować główny obszar roboczy na widoczny obszar roboczy. Kopiowanie obszaru roboczego jest tanie, a rysowanie jest drogie. Przenosząc wyróżnienia na osobny obszar roboczy, unikamy tego kosztu podczas włączania i wyłączania wyróżnień.

Kompleksowo przetestowana analiza logu czasu

Jedną z zalet tworzenia nowej funkcji od podstaw jest możliwość zastanowienia się nad wcześniejszymi wyborami technicznymi i wprowadzenia ulepszeń. Jedną z rzeczy, które chcieliśmy ulepszyć, było wyraźne podzielenie kodu na 2 niemal całkowicie odrębne części:

Przeanalizuj plik śledzenia i wyodrębnij wymagane dane. Renderowanie zestawu ścieżek.

Oddzielenie analizowania (część 1) od pracy interfejsu (część 2) umożliwiło nam stworzenie solidnego systemu analizowania. Każdy ślad jest przetwarzany przez serię procedur obsługi, które odpowiadają za różne aspekty: procedura LayoutShiftHandler oblicza wszystkie informacje potrzebne do określenia przesunięć układu, a procedura NetworkRequestsHandler zajmuje się wyłącznie wyodrębnianiem żądań sieciowych. Ten jawny etap analizowania, na którym różne moduły obsługi odpowiadają za różne części śladu, również okazał się korzystny: analizowanie śladu może być bardzo skomplikowane, a możliwość skupienia się na jednym problemie naraz jest bardzo pomocna.

Przeprowadziliśmy też kompleksowe testy analizowania śladów, nagrywając je w Narzędziach deweloperskich, zapisując je, a następnie wczytując w ramach naszego pakietu testowego. To świetnie, ponieważ możemy testować na prawdziwych śladach, a nie tworzyć ogromnych ilości fałszywych danych śledzenia, które mogłyby stać się przestarzałe.

Testowanie zrzutów ekranu w przypadku interfejsu Canvas

Wracając do tematu testowania, zwykle testujemy komponenty interfejsu, renderując je w przeglądarce i sprawdzając, czy działają zgodnie z oczekiwaniami. Możemy wysyłać zdarzenia kliknięcia, aby wywoływać aktualizacje, i sprawdzać, czy DOM generowany przez komponenty jest prawidłowy. To podejście sprawdza się w naszym przypadku, ale nie działa w przypadku renderowania na płótnie. Nie ma możliwości sprawdzenia płótna i określenia, co jest na nim narysowane. Dlatego nasze zwykłe podejście polegające na renderowaniu, a następnie wysyłaniu zapytań nie jest odpowiednie.

Aby zapewnić pewien zakres testów, zdecydowaliśmy się na testowanie zrzutów ekranu. Każdy test uruchamia obszar roboczy, renderuje ścieżkę, którą chcemy przetestować, a następnie robi zrzut ekranu elementu obszaru roboczego. Zrzut ekranu jest następnie przechowywany w naszej bazie kodu, a w przyszłości testy będą porównywać zapisany zrzut ekranu z wygenerowanym zrzutem. Jeśli zrzuty ekranu będą się różnić, test się nie powiedzie. Udostępniamy też flagę, która umożliwia uruchomienie testu i wymuszenie aktualizacji zrzutu ekranu, gdy celowo zmieniliśmy renderowanie i musimy zaktualizować test.

Testy zrzutów ekranu nie są idealne i są nieco nieprecyzyjne. Możesz tylko sprawdzić, czy cały komponent renderuje się zgodnie z oczekiwaniami, a nie bardziej szczegółowe stwierdzenia. Początkowo nadużywaliśmy ich, aby mieć pewność, że każdy komponent (HTML lub canvas) renderuje się prawidłowo. Spowodowało to znaczne spowolnienie naszego zestawu testów i doprowadziło do problemów, w których drobne, prawie nieistotne zmiany w interfejsie (takie jak subtelne zmiany kolorów lub dodanie marginesu między elementami) powodowały niepowodzenie wielu zrzutów ekranu i wymagały aktualizacji. Obecnie ograniczyliśmy używanie zrzutów ekranu i wykorzystujemy je tylko w przypadku komponentów opartych na obszarze roboczym. Ta równowaga sprawdza się u nas do tej pory.

Podsumowanie

Tworzenie nowego panelu ze statystykami wydajności było dla zespołu bardzo przyjemnym i pouczającym doświadczeniem. Dowiedzieliśmy się wiele o plikach śledzenia, pracy z obszarem roboczym i nie tylko. Mamy nadzieję, że nowy panel przypadnie Ci do gustu. Czekamy na Twoją opinię.

Więcej informacji o panelu Statystyki skuteczności znajdziesz w artykule Statystyki skuteczności: przydatne informacje o wydajności witryny.

Pobieranie kanałów podglądu

Rozważ używanie Chrome w wersji Canary, deweloperskiej lub beta jako domyślnej przeglądarki do programowania. Te kanały wersji testowych zapewniają dostęp do najnowszych funkcji Narzędzi deweloperskich, umożliwiają testowanie najnowocześniejszych interfejsów API platformy internetowej i pomagają znajdować problemy w witrynie, zanim zrobią to użytkownicy.

Kontakt z zespołem Narzędzi deweloperskich w Chrome

Skorzystaj z tych opcji, aby porozmawiać o nowych funkcjach, aktualizacjach lub innych kwestiach związanych z Narzędziami deweloperskimi.