Środowisko wykonawcze systemu Android (ART) zostało znacznie ulepszone w wersji Androida 8.0. Poniższa lista podsumowuje ulepszenia, których producenci urządzeń mogą oczekiwać w ART.
Równoczesne zagęszczanie śmieciarki
Jak ogłoszono na konferencji Google I/O, ART zawiera nowy, współbieżny moduł zbierający elementy bezużyteczne (GC) w systemie Android 8.0. Ten moduł zbierający kompaktuje stertę przy każdym uruchomieniu GC i podczas działania aplikacji, z tylko jedną krótką przerwą na przetwarzanie korzeni wątków. Oto jego zalety:
- GC zawsze kompaktuje stertę: średnio o 32% mniejsze rozmiary sterty w porównaniu do Androida 7.0.
- Kompaktowanie umożliwia lokalną alokację obiektu wskaźnika wątkowego: alokacje są o 70% szybsze niż w systemie Android 7.0.
- Oferuje o 85% krótsze czasy pauzy w teście porównawczym H2 w porównaniu do Androida 7.0 GC.
- Czasy pauzy nie skalują się już wraz z rozmiarem sterty; aplikacje powinny móc korzystać z dużych stert, nie martwiąc się o jank.
- Szczegóły implementacji GC - Czytaj bariery:
- Bariery odczytu to niewielka ilość pracy wykonanej dla każdego odczytanego pola obiektu.
- Są one zoptymalizowane w kompilatorze, ale mogą spowolnić niektóre przypadki użycia.
Optymalizacje pętli
W wersji Androida 8.0 ART wykorzystuje szeroką gamę optymalizacji pętli:
- Ograniczenia sprawdzają eliminacje
- Statyczny: w czasie kompilacji udowodniono, że zakresy mieszczą się w określonych granicach
- Dynamiczne: testy w czasie wykonywania zapewniają, że pętle pozostają w granicach (w przeciwnym razie należy zrezygnować)
- Eliminacje zmiennych indukcyjnych
- Usuń martwą indukcję
- Zastąp indukcję używaną tylko po pętli wyrażeniami w formie zamkniętej
- Eliminacja martwego kodu wewnątrz korpusu pętli, usuwanie całych pętli, które stały się martwe
- Redukcja siły
- Transformacje pętli: odwrócenie, zamiana, dzielenie, rozwijanie, unimodularne itp.
- SIMDyzacja (zwana także wektoryzacją)
Optymalizator pętli znajduje się we własnym przebiegu optymalizacyjnym w kompilatorze ART. Większość optymalizacji pętli jest podobna do optymalizacji i uproszczeń w innych miejscach. Wyzwania pojawiają się w przypadku niektórych optymalizacji, które przepisują CFG w bardziej skomplikowany sposób niż zwykle, ponieważ większość narzędzi CFG (patrz węzły.h) skupia się na budowaniu CFG, a nie na jego przepisywania.
Analiza hierarchii klas
ART w systemie Android 8.0 wykorzystuje analizę hierarchii klas (CHA), optymalizację kompilatora, która dewirtualizuje wywołania wirtualne w połączenia bezpośrednie w oparciu o informacje wygenerowane w wyniku analizy hierarchii klas. Wirtualne wywołania są drogie, ponieważ są realizowane w oparciu o wyszukiwanie w tabeli vtable i wymagają kilku zależnych obciążeń. Nie można także wstawiać połączeń wirtualnych.
Oto podsumowanie powiązanych ulepszeń:
- Dynamiczna aktualizacja stanu metody pojedynczej implementacji - pod koniec czasu łączenia klas, kiedy tabela vtable zostanie zapełniona, ART przeprowadza porównanie wpis po wpisie z tabelą vtable superklasy.
- Optymalizacja kompilatora — kompilator skorzysta z informacji o pojedynczej implementacji metody. Jeśli metoda A.foo ma ustawioną flagę pojedynczej implementacji, kompilator zdewirtualizuje wywołanie wirtualne do wywołania bezpośredniego i w rezultacie spróbuje wstawić wywołanie bezpośrednie.
- Unieważnienie skompilowanego kodu — również na koniec czasu łączenia klas, gdy aktualizowane są informacje o pojedynczej implementacji, jeśli metoda A.foo, która wcześniej miała pojedynczą implementację, ale ten status jest teraz unieważniana, cały skompilowany kod zależy od założenia, że metoda A. foo ma potrzebę pojedynczej implementacji, aby skompilowany kod został unieważniony.
- Deoptymalizacja — w przypadku skompilowanego kodu na żywo, który znajduje się na stosie, zostanie zainicjowana deoptymalizacja, aby wymusić unieważniony skompilowany kod w trybie interpretera, aby zagwarantować poprawność. Zastosowany zostanie nowy mechanizm deoptymalizacji będący hybrydą deoptymalizacji synchronicznej i asynchronicznej.
Wbudowane pamięci podręczne w plikach .oat
ART wykorzystuje teraz wbudowane pamięci podręczne i optymalizuje witryny połączeń, dla których istnieje wystarczająca ilość danych. Funkcja wbudowanej pamięci podręcznej rejestruje dodatkowe informacje o czasie wykonywania w profilach i wykorzystuje je do dodawania dynamicznych optymalizacji do kompilacji z wyprzedzeniem.
Układ Dex
Dexlayout to biblioteka wprowadzona w systemie Android 8.0 służąca do analizowania plików dex i porządkowania ich zgodnie z profilem. Dexlayout ma na celu wykorzystanie informacji o profilowaniu środowiska wykonawczego do zmiany kolejności sekcji pliku dex podczas bezczynnej kompilacji konserwacyjnej na urządzeniu. Grupując części pliku dex, do których często uzyskuje się dostęp, programy mogą uzyskać lepsze wzorce dostępu do pamięci dzięki lepszej lokalizacji, oszczędzając pamięć RAM i skracając czas uruchamiania.
Ponieważ informacje o profilu są obecnie dostępne dopiero po uruchomieniu aplikacji, dexlayout jest zintegrowany z kompilacją dex2oat na urządzeniu podczas bezczynnej konserwacji.
Usuwanie pamięci podręcznej Dex
Do Androida 7.0 obiekt DexCache posiadał cztery duże tablice, proporcjonalne do liczby niektórych elementów w DexFile, a mianowicie:
- ciągi znaków (jedno odniesienie na DexFile::StringId),
- typy (jedno odniesienie na DexFile::TypeId),
- metody (jeden natywny wskaźnik na DexFile::MethodId),
- pola (jeden natywny wskaźnik na DexFile::FieldId).
Tablice te zostały wykorzystane do szybkiego wyszukiwania obiektów, które wcześniej rozwiązaliśmy. W systemie Android 8.0 wszystkie tablice zostały usunięte z wyjątkiem tablicy metod.
Wydajność tłumacza
Wydajność interpretera znacznie wzrosła w wersji Androida 7.0 wraz z wprowadzeniem „mterp” – interpretera wyposażonego w podstawowy mechanizm pobierania/dekodowania/interpretowania napisany w języku asemblera. Mterp jest wzorowany na szybkim interpreterze Dalvik i obsługuje arm, arm64, x86, x86_64, mips i mips64. W przypadku kodu obliczeniowego mterp Arta jest mniej więcej porównywalny z szybkim interpreterem Dalvika. Jednak w niektórych sytuacjach może to być znacznie – a nawet dramatycznie – wolniejsze:
- Wywołaj wydajność.
- Manipulacja ciągami i inni intensywnie korzystający z metod uznawanych za nieodłączne elementy Dalvika.
- Wyższe zużycie pamięci stosu.
Android 8.0 rozwiązuje te problemy.
Więcej wklejania
Od wersji Androida 6.0 ART może wstawiać dowolne wywołania w tych samych plikach dex, ale może wstawiać tylko metody liści z różnych plików dex. Były dwa powody tego ograniczenia:
- Wstawianie z innego pliku dex wymaga użycia pamięci podręcznej dex tego innego pliku dex, w przeciwieństwie do wstawiania tego samego pliku dex, które mogłoby po prostu ponownie wykorzystać pamięć podręczną dex obiektu wywołującego. Pamięć podręczna dex jest potrzebna w skompilowanym kodzie dla kilku instrukcji, takich jak wywołania statyczne, ładowanie ciągu znaków lub ładowanie klas.
- Mapy stosu kodują jedynie indeks metody w bieżącym pliku dex.
Aby rozwiązać te ograniczenia, Android 8.0:
- Usuwa dostęp do pamięci podręcznej Dex ze skompilowanego kodu (zobacz także sekcję „Usuwanie pamięci podręcznej Dex”)
- Rozszerza kodowanie mapy stosu.
Ulepszenia synchronizacji
Zespół ART dostroił ścieżki kodu MonitorEnter/MonitorExit i zmniejszył naszą zależność od tradycyjnych barier pamięci w ARMv8, zastępując je nowszymi instrukcjami (pozyskania/wydania), tam gdzie to możliwe.
Szybsze metody natywne
Szybsze natywne wywołania interfejsu Java Native Interface (JNI) są dostępne przy użyciu adnotacji @FastNative
i @CriticalNative
. Te wbudowane optymalizacje środowiska wykonawczego ART przyspieszają przejścia JNI i zastępują obecnie przestarzałą notację !bang JNI . Adnotacje nie mają wpływu na metody inne niż natywne i są dostępne tylko dla kodu języka Java platformy w bootclasspath
(bez aktualizacji Sklepu Play).
Adnotacja @FastNative
obsługuje metody niestatyczne. Użyj tej opcji, jeśli metoda uzyskuje dostęp do jobject
jako parametr lub wartość zwracaną.
Adnotacja @CriticalNative
zapewnia jeszcze szybszy sposób uruchamiania metod natywnych, z następującymi ograniczeniami:
- Metody muszą być statyczne — nie mogą mieć obiektów dla parametrów, wartości zwracanych ani ukrytej metody
this
. - Do metody natywnej przekazywane są tylko typy pierwotne.
- Metoda natywna nie używa parametrów
JNIEnv
ijclass
w swojej definicji funkcji. - Metoda musi być zarejestrowana w
RegisterNatives
zamiast polegać na dynamicznym łączeniu JNI.
@FastNative
może poprawić wydajność metody natywnej do 3x, a @CriticalNative
do 5x. Na przykład przejście JNI zmierzone na urządzeniu Nexus 6P:
Wywołanie natywnego interfejsu Java (JNI). | Czas wykonania (w nanosekundach) |
---|---|
Zwykły JNI | 115 |
!bang JNI | 60 |
@FastNative | 35 |
@CriticalNative | 25 |