ART - największa zmiana w Androidzie od czasu Androida [cz. 3]

ART - największa zmiana w Androidzie od czasu Androida [cz. 3]

ART - największa zmiana w Androidzie od czasu Androida [cz. 3]
Michał Mynarski
10.08.2014 13:05, aktualizacja: 10.08.2014 15:34

Po dłuższej przerwie czas wprowadzić Was w tajniki działania ART. Co takiego zrobiło Google, że Android RunTime spisuje się tak dobrze. Dlaczego ma szansę być remedium na bolączki tego systemu?

Czym jest ART?

Android RunTime od Dalvika różni się tym, że kompiluje cały kod aplikacji podczas instalacji. To powinno zredukować lub wyeliminować całkowicie lag występujący podczas otwierania zainstalowanej aplikacji (gdy Dalvik dostał pierwszą, największą porcję kodu do interpretacji). Dalej będzie już tylko lepiej - jako, że mamy kod całej aplikacji już skompilowany pod dany SoC, eliminuje się również proces "dointerpretowania" kolejnych porcji kodu, dzięki czemu program będzie działać lżej, szybciej i stabilniej.

Inną zaletą korzystania z Android RunTime to oszczędzanie procesora. Kod skompilowany bez sporego narzutu interpretatora nie tylko działa szybciej, ale do jego obrobienia wymagana jest mniejsza praca samego procesora. To z kolei implikuje niższe zużycie energii i mniej wydzielanego ciepła.

Czas zajrzeć ART pod maskę

Android RunTime został zaprojektowany tak by być w pełni kompatybilnym z formatem *dex, czyli bytecodem Dalvika, o którym pisałem w części drugiej. Dla deweloperów nie będzie więc odczuwalne czy ich aplikacje będą odpalane na ART czy starej maszynie wirtualnej.

Ponieważ w przypadku ART kod jest optymalizowany tylko raz (w całości) można zastosować sztuczki optymalizacyjne, których próżno szukać przy kompilacji Just In Time, gdzie kompilowany jest tylko jego kawałek. Jeżeli Google nie pokpiło sprawy kompilacji, zyski takiego rozwiązania będą widoczne dla każdego użytkownika. Nie chodzi tu tylko o dłuższy czas działania na baterii - ze względu na brak procesu interpretacji i "dokompilowywania" kodu zmniejsza się ilość potrzebnych cykli procesora, a zarządzanie pamięcią jest bardziej efektywne (o czym później).

Cykl życia aplikacji pod ART z pominięciem resources i kodu natywnego.
Cykl życia aplikacji pod ART z pominięciem resources i kodu natywnego.© własne

W efekcie procesu dex2oat (podczas instalacji) z pliku o rozszerzeniu *dex powstaje plik *elf, który jest wykonywany przez ART.

Jedyną złą stroną kompilacji Ahead of Time jest czas potrzebny na pełną instalację - jest on znacznie dłuższy niż ten w przypadku JIT. Google obiecuje jednak, że zmiana ta nie będzie "dramatyczna". Co więcej - zespół odpowiedzialny za rozwój ART wciąż pracuje by ten aspekt poprawić, a może nawet uczynić cały proces szybszym niż w przypadku Dalvika (chociaż osobiście wątpię by było to możliwe).

Zarządzanie pamięcią

Języki programowania wysokiego poziomu często są uzbrojone w mechanizmy automatycznego zarządzania pamięcią. W świecie Apple'a i Objective C jest to przykładowo ARC (Auto Reference Counting), a w przypadku Androida i Javy - Garbage Collector (w skrócie GC).

GC istnieje w ekosystemie od jego początku. Dzięki niemu programista pisząc aplikację nie musi ręcznie alokować (zajmować) pamięci na obiekty czy uwalniać jej, gdy obiekt przestaje być potrzebny (te praktyki są normą dla piszących programy niskopoziomowo). Wadą takiego rozwiązania jest brak kontroli - autor kodu musi zdać się na kaprysy i zachcianki systemu wierząc, że w poprawny sposób będzie on kontrolował pamięć.

Dalvik i Android sporo się nacierpiały przez GC - za każdym razem gdy aplikacja potrzebuje więcej pamięci do zaalokowania oraz kopiec/sterta aplikacji (czyli pamięć dla niej zarezerwowana) jest już zapełniony, Garbage Collector jest "odpalany". Jego praca polega na przejrzeniu całej sterty pamięci, ponumerowaniu wszystkich obiektów używanych przez aplikację, oznaczeniu tych, które ciągle są w użyciu i uwolnieniu (dealokowanie) pozostałych.

W Davliku ten proces powoduje dwie pauzy. Pierwsza ma miejsce podczas fazy numeracji, a druga drugą podczas fazy oznaczania. W praktyce wykonywanie kodu podczas tych przerw jest zawieszane, na każdym z wątków, na których aplikacja jest uruchomiona. Jeżeli pauza trwa za długo, gubione są klatki animacji przy renderowaniu widoku aplikacji, co dla użytkownika objawia się poprzez znane wszystkim "lagi".

Google twierdzi, że na Nexusie 5 średni czas trwania tych przerw to 54 ms, co w efekcie daje co najmniej 4 zgubione klatki animacji. Za każdym razem, gdy Garbage Collector się aktywuje.

Andrei Frumusanu:

Z własnego doświadczenia wiem, że te czasy mogą drastycznie wzrosnąć podczas zależnie od programu. Np. oficjalna aplikacja FIFA to znakomity przykład, gdzie GC wymyka się spoza kontroli.

Poniżej widzicie logi tego jak działa GC. System ten został tutaj łącznie wywołany 9 razy, i spowodował łącznie 603 ms przerwy w działaniu aplikacji, co dało w sumie 214 zgubionych klatek animacji. Większość z pauz było spowodowane żądaniami o alokację pamięci (GC_FOR_ALLOC).

Wywołania Garbage Collectora pod Dalvikiem.
Wywołania Garbage Collectora pod Dalvikiem.© anandtech.com

A teraz spójrzmy na te same logi, tylko z uruchomionym ART:

Wywołania Garbage Collectora pod ART.
Wywołania Garbage Collectora pod ART.© anandtech.com

Różnica jest ogromna - nowy RunTime zarządza pamięcią na tyle skutecznie, iż łącznie na rzecz alokacji pamięci przeznaczono 12.36 ms w czterech wezwaniach "pierwszoplanowych" (ang. foreground) i dwóch "w tle" (ang. background). Łącznie zgubionych klatek animacji było 63.

Andrei Frumusanu:

Oczywiście to jest najgorszy możliwy przypadek - fatalnie napisanej aplikacji, która nawet pod ART gubi tyle klatek, aczkolwiek praktyki programistyczne tj. przesadne obciążanie wątku odpowiedzialnego za graficzny interfejs to coś, z czym Android boryka się od dawien dawna.

Jak ART to robi?

Kolokwialnie mówiąc - nowy kompilator bierze na klatę część pracy, którą zazwyczaj zajmuje się Garbage Collector, co eliminuje potrzebę przerwy podczas fazy numerowania. Druga pauza jest potężnie zredukowana dzięki próbie wykonania czynności przed przerwą techniką zwaną packard pre-cleaning, zaś podczas samej przerwy sprawdzane jest tylko, czy całość została wykonana poprawnie.

Nie bez znaczenia są również zmiany dotyczące zarządzania w pamięci dużymi obiektami (tj. bitmapy, obrazki etc). Nowy typ danych wprowadzony specjalnie dla nich (Large Object Space), odseparowuje je od głównej sterty pamięci. Wcześniej tak duże obiekty powodowały sporo problemów, w tym fragmentację sterty pamięci, co prowadziło do zwiększonej ilości wywołań Garbage Collectora. Wraz z wprowadzeniem LOS ten problem został zminimalizowany - ART zadba o odpowiednią alokację i zniszczenie tych obiektów, więc nie wpłyną negatywnie na spójność sterty.

Czy to już wszystko? Absolutnie nie

Przebudowy doczekał się również sam system alokacji pamięci. Podobno ART samo w sobie odpowiadało za 25% wzrost wydajności względem Dalvika, ale Google uznało, że to za mało.

Dlatego zrezygnowano z aktualnego mechanizmu alokowania pamięci na rzecz nowego, który określany jest jako Runs-of-Slots-Allocator. Został on stworzony od początku do końca pod kątem wielowątkowych aplikacji - rosalloc potrafi rezerwować dużo mniejsze porcje pamięci. Efektem ma być nawet 10 krotnie szybsza alokacja.

Również algorytmy samego Garbage Collectora zostały poprawione tak, by unikać przerw w działaniu aplikacji. Google wciąż je dopracowuje, a ostatnio wprowadzony został nowy "Moving garbage collector", którego zadaniem jest defragmentacja sterty pamięci aplikacji, które działają w tle.

W części czwartej przeczytacie co ART zmienia w kwestii 64 bitów, a także rzucimy okiem na ciekawe wyniki testów wydajności. Stay tuned!

Źródło artykułu:WP Komórkomania
Oceń jakość naszego artykułuTwoja opinia pozwala nam tworzyć lepsze treści.
Wybrane dla Ciebie
Komentarze (0)