Studia przypadków

Grab zmniejsza liczbę błędów ANR o 32% i skraca czas uruchamiania o 35% dzięki R8 i optymalizacji na podstawie profilu

Czas czytania: 5 min

Grab to najpopularniejsza w Azji Południowo-Wschodniej superaplikacja, która oferuje usługi dostawy, transportu i finansowe milionom użytkowników w 8 krajach. Zespół Graba, który dysponuje 10-letnią bazą kodu zawierającą ponad 10 milionów wierszy kodu i setki bibliotek innych firm utworzonych przy użyciu hybrydowej konfiguracji Bazel i Gradle, musiał stawić czoła 3 narastającym wyzwaniom: wysokiej częstotliwości błędów ANR, rozmiarowi aplikacji, który stale rósł wraz z dodawaniem nowych usług, oraz czasowi uruchamiania, który z roku na rok powoli się wydłużał. Dzięki włączeniu optymalizacji R8, doprecyzowaniu reguł zachowywania oraz wdrożeniu profili podstawowych i profili uruchamiania firma Grab zmniejszyła częstotliwość błędów ANR widocznych dla użytkowników o 32%, rozmiar pobieranej aplikacji o około 30% i czas uruchamiania o 35%.

Eliminowanie skoków liczby błędów ANR po opublikowaniu aplikacji w celu zapewnienia lepszych wrażeń użytkownikom

Przed optymalizacją procesu kompilacji firma Grab borykała się z opóźnieniami przy uruchamianiu aplikacji na zimno i skokami liczby błędów „Aplikacja nie odpowiada” (ANR) bezpośrednio po nowych aktualizacjach. Wynikało to głównie z obciążenia związanego z odczytywaniem wielu plików DEX podczas inicjowania aplikacji. 

Profile w chmurze w Google Play zapewniały optymalizację standardowych procesów w całej bazie użytkowników, ale nie optymalizowały wszystkich ścieżek inicjowania wymaganych w przypadku głównych ścieżek użytkownika w aplikacji Grab, takich jak inicjowanie wykresów zależności Dagger podczas uruchamiania aplikacji. W przypadku ścieżek kodu, które nie zostały uwzględnione w początkowej dystrybucji, urządzenia korzystały z kompilacji JIT i lokalnej optymalizacji w tle w kolejnych dniach. Wdrożenie niestandardowego profilu Baseline rozwiązało ten problem, ponieważ te główne ścieżki użytkownika zostały udostępnione bezpośrednio w profilach Cloud w Google Play. Pozwoliło to rozszerzyć zakres optymalizacji, dzięki czemu te kluczowe ścieżki były kompilowane do kodu maszynowego natychmiast po instalacji. 

ANDDM_Grab_Inline_Quote_ALT.png

„W przypadku każdej cotygodniowej wersji aplikacji regularnie obserwowaliśmy skoki częstotliwości błędów ANR” – wyjaśnia Nguyen Van Minh, główny inżynier oprogramowania w zespole Grab Passenger App Core. 

Aby wyeliminować błędy ANR i skrócić czas uruchamiania, firma Grab wdrożyła optymalizacje R8, profile podstawowe i profile uruchamiania. Optymalizacja na podstawie profilu wraz z optymalizatorem R8 zapewniła natychmiastową poprawę czasu uruchamiania o 12% dzięki R8 i dodatkowe 15% dzięki profilom podstawowym. Pozwoliło to skrócić czas uruchamiania o 27% i skutecznie wyeliminować skoki liczby błędów ANR po opublikowaniu aplikacji.

Firma Grab przeprowadziła optymalizację aplikacji w 3 strategicznych etapach: włączenie optymalizacji R8 w trybie zgodności, przejście na pełny tryb R8, a następnie doprecyzowanie reguł zachowywania za pomocą analizatora konfiguracji i dodanie zarządzania CI/CD.

Etap 1. Włączanie optymalizacji R8  (wprowadzone w 2025 r.)

Na pierwszym etapie firma Grab zrezygnowała ze starszego domyślnego pliku Proguard na rzecz pliku proguard-android-optimize.txt, który umożliwia optymalizacje R8. Ponieważ baza kodu miała ponad 10 lat i zawierała ponad 10 milionów wierszy kodu, włączenie optymalizatora wymagało rozwiązania problemów ze starszymi konfiguracjami i użyciem refleksji.

Aby zmaksymalizować efekt tej fazy, firma Grab opublikowała aktualizację aplikacji z włączonym trybem zgodności R8 z profilami podstawowymi i profilami uruchamiania. Te połączone działania przyniosły natychmiastowe efekty: częstotliwość błędów ANR widocznych dla użytkowników spadła o 25%, rozmiar pobieranej aplikacji zmniejszył się o 16%, a ogólny czas uruchamiania „na zimno” skrócił się o 27%.

Etap 2. Pełny tryb R8 i migracja (wdrożenie w 2026 r.)

Ten etap, który rozpoczął się 9 miesięcy po pierwszym wdrożeniu, polegał na przełączeniu aplikacji na tryb pełny R8, co umożliwiło dalsze optymalizacje. Standardowe biblioteki, takie jak Retrofit, Firebase i Jetpack Compose, działały bezproblemowo z R8 dzięki wbudowanym regułom, ale migracja pozostałej części ogromnej bazy kodu wymagała rozwiązania problemów ze ścieżkami kodu, które intensywnie korzystają z refleksji:

Aby przejść na tryb pełny R8, zespół zastosował reguły zachowywania konsumentów z najnowszej wersji Gson, co zapewniło bezproblemowe analizowanie plików JSON. Rozwiązaliśmy też problemy z optymalizacją związane z dynamicznymi serwerami proxy w języku Java, które używają refleksji do generowania implementacji interfejsu w czasie działania. R8 nie może wykryć tego użycia środowiska wykonawczego podczas kompilacji, więc usuwa lub zmienia nazwy niezbędnych klas, co powoduje awarię aplikacji. Zamiast stosować ogólne wyjątki, które zwiększyłyby rozmiar aplikacji, zespół wyodrębnił konkretne klasy korzystające z refleksji i wdrożył precyzyjne reguły zachowywania, co pozwoliło zachować stabilność aplikacji i skuteczność optymalizacji.

Ostatecznie pełny tryb R8 przyniósł ogromne, skumulowane ulepszenia, w tym dodatkowe zmniejszenie rozmiaru pobieranej aplikacji o 14%, skrócenie czasu uruchamiania o 10% i zmniejszenie liczby błędów ANR widocznych dla użytkowników o kolejne 10%. Po przejściu na pełny tryb R8 baza kodu Graba osiągnęła te wyniki optymalizacji R8. 

  • Wynik optymalizacji: 72%
  • Wynik zmniejszania rozmiaru: 73%
  • Wynik zaciemnienia kodu: 79,6%

Te wyniki dostarczyły informacji o konfiguracji R8 i potrzebie doprecyzowania reguł zachowywania, aby zmaksymalizować optymalizację R8. 

Etap 3. Dostosowywanie reguł przechowywania 

W ostatniej fazie optymalizacji firma Grab skupia się na zapewnieniu długoterminowej kondycji bazy kodu, ciągłym monitorowaniu wydajności i ścisłym zarządzaniu kompilacją. Aby systematycznie mierzyć higienę reguł i zapobiegać regresjom konfiguracji w trakcie kolejnych cykli wydawniczych, zespół integruje analizator konfiguracji R8 z procesem oceny. To narzędzie zapewnia szczegółowy wgląd w to, jak skutecznie reguły zachowywania Graba umożliwiają R8 optymalizację, zmniejszanie i zaciemnianie aplikacji.

Dzięki wykorzystaniu podstaw trybu pełnego R8 i metodycznemu usuwaniu starszych reguł zachowywania firma Grab osiągnęła znaczne ulepszenia we wszystkich 3 głównych wymiarach ocenianych przez analizator:

  • Wynik optymalizacji: wzrost z 72% do 78,2%
  • Wynik zmniejszania rozmiaru: wzrasta z 73% do 79,1%
  • Wynik zaciemnienia kodu: wzrasta z 79,6% do 81,7%

Oprócz poprawy tych wyników diagnostycznych ta specjalna faza ulepszania reguł zachowywania pozwala dodatkowo zmniejszyć rozmiar aplikacji do pobrania o około 3, 6%. Co ważniejsze, śledzenie tych danych dostarcza zespołowi inżynierów obiektywnych danych potrzebnych do wdrożenia automatycznych zabezpieczeń CI przed scaleniem, dzięki czemu nowo wprowadzony kod jest zgodny z rygorystycznymi standardami reguł zachowywania, a trudno wypracowane korzyści związane z wydajnością są chronione przez długi czas.

Wyniki

Najważniejsze ulepszenia wydajności po wdrożeniu optymalizatora R8:

Faza optymalizacjiBłędy ANRRozmiarUruchomienie „na zimno”
Faza 1. Optymalizator R8 (wdrożony w 2025 r.)~25% reductionZmniejszenie o 16%Poprawa o około 27%
Etap 2. Tryb pełny R8 (wprowadzony w 2026 r.)10% reduction14% redukcjiWzrost o 10%
Optymalizacja reguł przechowywania-3,6%-
Ogólnie (łącznie)~32,5% reductionZmniejszenie o około 30,4%~35% poprawy

Problemy techniczne 

Włączenie optymalizatora R8 w bazie kodu Graba, która ma już 10 lat, spowodowało poważne problemy techniczne na dużą skalę:

  • Testowanie: 10 lat gromadzenia odbić światła spowodowało powstanie przypadków skrajnych przy pełnej optymalizacji. Ponieważ wyczerpujące testowanie milionów wierszy kodu było niemożliwe, zespół zastosował podejście przyrostowe o wysokim priorytecie – skupił się na testowaniu kodu objętego zawężanymi regułami zachowywania i iteracyjnie dopracowywał te reguły, aby kierować je tylko na konkretne klasy, metody lub pola wymagane do refleksji, co pozwoliło zmaksymalizować optymalizację R8.
  • Czas oczekiwania na kompilację i architektura hybrydowa: hybrydowy system kompilacji Graba (Bazel do debugowania, Gradle do kompilacji do publikacji R8) oznaczał, że eksperymentalne zmiany R8 wymagały do 2 godzin w zdalnej CI. Aby rozwiązać problem z brakiem informacji zwrotnych, firma Grab dostosowała codzienne kompilacje QA do zachowania produkcyjnego R8 i zautomatyzowała zbieranie kluczowych artefaktów kompilacji CI, takich jak mapping.txt i configuration.txt, co usprawniło analizę.
  • Powolne cykle debugowania: śledzenie głównych przyczyn w setkach zoptymalizowanych klas było dużym obciążeniem dla niewielkiego zespołu. Firma Grab rozwiązała ten problem, automatyzując przesyłanie plików mapowania do Firebase Crashlytics za pomocą Gradle i grupując mapowania do odczytywania zaciemnionego kodu na potrzeby Konsoli Play, co umożliwiło natychmiastowe i dokładne rozwiązywanie problemów z symbolami w czasie działania aplikacji.

Długoterminowy wpływ kodu z R8

Oprócz natychmiastowego wzrostu wydajności włączenie optymalizatora R8 przyniosło trwałą korzyść strukturalną w przypadku ogromnej bazy kodu aplikacji Grab. Zespół Grab użył narzędzia open source app-sizer, aby zmierzyć wpływ R8, i zauważył, że R8 trwale zmniejsza rozmiar pobierania każdej linijki kodu w aplikacji.

ANDDM_Grab_Inline_Metric.png

W erze programowania wspomaganego przez AI, gdy ilość kodu szybko rośnie, ta optymalizacja jest jeszcze ważniejsza. Bez R8 rozszerzenie kodu proporcjonalnie zwiększa rozmiar pobieranych danych. R8 zapewnia, że przy tym samym współczynniku rozszerzania kodu wykorzystanie pamięci jest znacznie mniejsze.

Uwaga: metodologia: udział Kotlin/Java w rozmiarze aplikacji do pobrania (reprezentatywne urządzenie) według narzędzia app-sizer podzielony przez liczbę wierszy kodu Kotlin w wersji produkcyjnej (cloc; tylko główne zbiory źródeł; z wyłączeniem modułów testowych, próbnych i makiet).

Zarządzanie regułami i zasady ochrony w Keep

Aby zapobiec pogorszeniu działania reguł i utrzymać optymalizacje R8 w czasie, Grab planuje zintegrować automatyczne zabezpieczenia przed scaleniem bezpośrednio z potokiem CI. Zgodnie z tymi zasadami nowe reguły przechowywania są ściśle ograniczone, a wszelkie dodatkowe reguły wymagają wyraźnego uzasadnienia i sprawdzenia. Zespół Grab śledzi wyniki optymalizacji R8 w CI. Zespół planuje wykorzystać te dane jako ciągłą pętlę informacji zwrotnych, aby wykrywać regresje przed scaleniem i zachować korzyści z optymalizacji w każdym cyklu wydawniczym. 

R8-rules-score (1).png

Rysunek 1. Panel CI śledzący wyniki zmniejszania, optymalizacji i zaciemniania kodu przez R8 w kompilacjach Grab, aby aktywnie wykrywać regresje i zapobiegać im w kolejnych wersjach.

Podsumowanie 

W obliczu wyzwań związanych z ogromną, rozwijającą się bazą kodu zespół Pax App Core firmy Grab aktywnie zajął się problemem wydłużającego się czasu uruchamiania i skoków wydajności po opublikowaniu aplikacji, wdrażając profile podstawowe i stale ulepszając konfigurację R8. Zmniejszyliśmy liczbę błędów ANR widocznych dla użytkowników o ok. 32,5%, rozmiar pobieranej aplikacji o ok. 30,3% i czas uruchomienia „na zimno” o ok. 35%. Jednocześnie znacznie poprawiliśmy wydajność skalowania bazy kodu.

Te optymalizacje zapewniły milionom użytkowników w Azji Południowo-Wschodniej szybsze i bardziej stabilne działanie superaplikacji, a inżynierom Graba dały narzędzia do szybszego tworzenia funkcji bez ryzyka gwałtownego wzrostu rozmiaru pobieranych plików.

Aby dowiedzieć się więcej o R8 i zacząć korzystać z tego narzędzia, wejdź na - d.android.com/r8.

Autor:
Czytaj dalej