Ręczne tworzenie profili bazowych i pomiar ich skuteczności

Zdecydowanie zalecamy automatyzację generowania reguł profilu za pomocą biblioteki Jetpack Macrobenchmark, aby zmniejszyć nakład pracy ręcznej i zwiększyć ogólną skalowalność. Możesz jednak ręcznie tworzyć i mierzyć reguły profilu w aplikacji.

Ręczne definiowanie reguł profilu

Reguły profilu możesz zdefiniować ręcznie w aplikacji lub module biblioteki, tworząc plik o nazwie baseline-prof.txt w katalogu src/main. Jest to ten sam folder, który zawiera plik AndroidManifest.xml.

Plik zawiera po jednej regule w każdym wierszu. Każda reguła reprezentuje wzorzec dopasowywania metod lub klas w aplikacji lub bibliotece, które wymagają optymalizacji.

Składnia tych reguł jest nadzbiorem formatu profilu ART zrozumiały dla człowieka (HRF) w przypadku używania adb shell profman --dump-classes-and-methods. Składnia jest podobna do składni deskryptorów i sygnatur, ale umożliwia używanie symboli wieloznacznych, co upraszcza proces tworzenia reguł.

Poniższy przykład pokazuje kilka reguł profilu Baseline zawartych w bibliotece Jetpack Compose:

HSPLandroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
HSPLandroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
HLandroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
PLandroidx/compose/runtime/CompositionImpl;->applyChanges()V
HLandroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
Landroidx/compose/runtime/ComposerImpl;

Możesz spróbować zmodyfikować reguły profilu w tym przykładowym projekcie Compiler Explorer. Pamiętaj, że Compiler Explorer obsługuje tylko format profilu ART zrozumiały dla człowieka (HRF), więc symbole wieloznaczne nie są obsługiwane.

Składnia reguł

Te reguły mogą mieć jedną z 2 form, aby kierować na metody lub klasy:

[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]

Reguła klasy używa tego wzorca:

[CLASS_DESCRIPTOR]

Szczegółowe informacje znajdziesz w tej tabeli:

Składnia Opis
FLAGS Reprezentuje co najmniej 1 ze znaków H, S i P, aby wskazać, czy ta metoda musi być oznaczona jako Hot, Startup lub Post Startup w odniesieniu do typu uruchamiania.

Metoda z flagą H wskazuje, że jest to metoda „hot”, co oznacza, że jest wywoływana wiele razy w ciągu cyklu życia aplikacji.

Metoda z flagą S wskazuje, że jest to metoda wywoływana podczas uruchamiania.

Metoda z flagą P wskazuje, że jest to metoda wywoływana po uruchomieniu.

Klasa obecna w tym pliku wskazuje, że jest używana podczas uruchamiania i musi zostać wstępnie przydzielona w stercie, aby uniknąć kosztów wczytywania klasy. Kompilator ART stosuje różne strategie optymalizacji, takie jak kompilacja AOT tych metod i optymalizacja układu w wygenerowanym pliku AOT.
CLASS_DESCRIPTOR Deskryptor klasy metody docelowej. Na przykład androidx.compose.runtime.SlotTable ma deskryptor Landroidx/compose/runtime/SlotTable;. Zgodnie z formatem Dalvik Executable (DEX) na początku dodawana jest litera L.
METHOD_SIGNATURE Sygnatura metody, w tym nazwa, typy parametrów i zwracane typy metody. Na przykład:

// LayoutNode.kt

fun isPlaced():Boolean {
// ...
}

na LayoutNode ma podpis isPlaced()Z.

Te wzorce mogą zawierać symbole wieloznaczne, dzięki czemu jedna reguła może obejmować wiele metod lub klas. Aby uzyskać pomoc dotyczącą pisania z użyciem składni reguł w Android Studio, zapoznaj się z wtyczką Android Baseline Profiles.

Przykładowa reguła z symbolem wieloznacznym może wyglądać tak:

HSPLandroidx/compose/ui/layout/**->**(**)**

Obsługiwane typy w regułach profilu podstawowego

Reguły profilu Baseline obsługują te typy. Szczegółowe informacje o tych typach znajdziesz w artykule o formacie Dalvik Executable (DEX).

Znak Typ Opis
B bajt Bajt ze znakiem
C char Punkt kodowy znaku Unicode zakodowany w formacie UTF-16
D liczba zmiennoprzecinkowa Wartość zmiennoprzecinkowa podwójnej precyzji
F liczba zmiennoprzecinkowa Wartość zmiennoprzecinkowa pojedynczej precyzji
I int Liczba całkowita
J długi Długa liczba całkowita
S krótki Podpisany krótki
V unieważnienie, Unieważnienie
Z wartość logiczna Prawda czy fałsz?
L (nazwa zajęć) odniesienie Instancja nazwy klasy

Dodatkowo biblioteki mogą definiować reguły, które są pakowane w artefaktach AAR. Gdy tworzysz plik APK, aby uwzględnić te artefakty, reguły są scalane (podobnie jak w przypadku scalania plików manifestu) i kompilowane w kompaktowy binarny profil ART, który jest specyficzny dla pliku APK.

ART wykorzystuje ten profil, gdy plik APK jest używany na urządzeniach, aby skompilować określony podzbiór aplikacji w trybie AOT podczas instalacji na Androidzie 9 (poziom API 28) lub Androidzie 7 (poziom API 24) przy użyciu ProfileInstaller.

Ręczne zbieranie profili podstawowych

Możesz ręcznie wygenerować profil podstawowy bez konfigurowania biblioteki Macrobenchmark i utworzyć automatyzacje interfejsu dla głównych ścieżek użytkownika. Zalecamy korzystanie z testów Macrobenchmark, ale nie zawsze jest to możliwe. Jeśli na przykład używasz systemu kompilacji innego niż Gradle, nie możesz używać wtyczki Baseline Profile do Gradle. W takich przypadkach możesz ręcznie zebrać reguły profilu Baseline. Jest to znacznie łatwiejsze, jeśli używasz urządzenia lub emulatora z interfejsem API na poziomie 34 lub wyższym. Chociaż jest to nadal możliwe na niższych poziomach interfejsu API, wymaga dostępu do roota i użycia emulatora z obrazem AOSP. Możesz zbierać reguły bezpośrednio, wykonując te czynności:

  1. Zainstaluj wersję do publikacji aplikacji na urządzeniu testowym. Aby można było przechwycić profil, który może być używany przez system kompilacji, rodzaj kompilacji aplikacji nie może być zoptymalizowany pod kątem R8 i nie może być z możliwością debugowania.
  2. Wyłącz instalację profilu i zamknij aplikację.

    Jeśli plik APK jest zależny od biblioteki Jetpack Profile Installer, biblioteka ta uruchamia profil przy pierwszym uruchomieniu pliku APK. Może to zakłócać proces generowania profilu, więc wyłącz tę funkcję za pomocą tego polecenia:

    adb shell am broadcast -a androidx.profileinstaller.action.SKIP_FILE WRITE_SKIP_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
  3. Zresetuj kompilację aplikacji i wyczyść wszystkie profile.

    Interfejs API na poziomie 34 lub wyższym

    adb shell cmd package compile -f -m verify $PACKAGE_NAME
    adb shell pm art clear-app-profiles $PACKAGE_NAME

    Interfejs API na poziomie 33 lub niższym

    adb root
    adb shell cmd package compile --reset $PACKAGE_NAME

  4. Uruchom aplikację i ręcznie przejdź przez główne ścieżki użytkownika, dla których chcesz zebrać profil.

  5. Odczekaj co najmniej 5 sekund, aż profile się ustabilizują.

  6. Wykonaj działanie zapisu i poczekaj na jego zakończenie. Jeśli plik APK jest zależny od biblioteki Jetpack Profile Installer, użyj jej do zrzutu profili:

    adb shell am broadcast -a androidx.profileinstaller.action.SAVE_PROFILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
    sleep 1 # wait 1 second
    adb shell am force-stop $PACKAGE_NAME
    Jeśli nie używasz instalatora profilu, zrzuć profile ręcznie w emulatorze za pomocą tego polecenia:

    adb root
    adb shell killall -s SIGUSR1 $PACKAGE_NAME
    sleep 1 # wait 1 second
    adb shell am force-stop $PACKAGE_NAME

  7. Przekonwertuj wygenerowane profile binarne na tekst:

    Interfejs API na poziomie 34 lub wyższym

    adb shell pm dump-profiles --dump-classes-and-methods $PACKAGE_NAME

    Interfejs API na poziomie 33 lub niższym

    Sprawdź, czy został utworzony profil referencyjny lub bieżący. Profil referencyjny znajduje się w tej lokalizacji:

    /data/misc/profiles/ref/$$PACKAGE_NAME/primary.prof

    Bieżący profil znajduje się w tej lokalizacji:

    /data/misc/profiles/cur/0/$PACKAGE_NAME/primary.prof

    Określ lokalizację pliku APK:

    adb root
    adb shell pm path $PACKAGE_NAME

    Wykonaj konwersję:

    adb root
    adb shell profman --dump-classes-and-methods --profile-file=$PROFILE_PATH --apk=$APK_PATH > /data/misc/profman/$PACKAGE_NAME-primary.prof.txt

  8. Użyj adb, aby pobrać zrzucony profil z urządzenia:

    adb pull /data/misc/profman/$PACKAGE_NAME-primary.prof.txt PATH_TO_APP_MODULE/src/main/

Spowoduje to pobranie wygenerowanych reguł profilu i zainstalowanie ich w module aplikacji. Przy następnym kompilowaniu aplikacji zostanie uwzględniony profil Baseline. Aby to sprawdzić, wykonaj czynności opisane w sekcji Problemy z instalacją.

Ręczne mierzenie ulepszeń aplikacji

Zdecydowanie zalecamy mierzenie ulepszeń aplikacji za pomocą analizy porównawczej. Jeśli jednak chcesz mierzyć ulepszenia ręcznie, możesz zacząć od zmierzenia niezoptymalizowanego uruchamiania aplikacji, aby mieć punkt odniesienia.

PACKAGE_NAME=com.example.app
# Force Stop App
adb shell am force-stop $PACKAGE_NAME
# Reset compiled state
adb shell cmd package compile --reset $PACKAGE_NAME
# Measure App startup
# This corresponds to `Time to initial display` metric.
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

Następnie zainstaluj profil podstawowy z nieoficjalnego źródła.

# Unzip the Release APK first.
unzip release.apk
# Create a ZIP archive.
# The name should match the name of the APK.
# Copy `baseline.prof{m}` and rename it `primary.prof{m}`.
cp assets/dexopt/baseline.prof primary.prof
cp assets/dexopt/baseline.profm primary.profm
# Create an archive.
zip -r release.dm primary.prof primary.profm
# Confirm that release.dm only contains the two profile files:
unzip -l release.dm
# Archive:  release.dm
#   Length      Date    Time    Name
# ---------  ---------- -----   ----
#      3885  1980-12-31 17:01   primary.prof
#      1024  1980-12-31 17:01   primary.profm
# ---------                     -------
#                               2 files
# Install APK + Profile together.
adb install-multiple release.apk release.dm

Aby sprawdzić, czy pakiet został zoptymalizowany podczas instalacji, uruchom to polecenie:

# Check dexopt state.
adb shell dumpsys package dexopt | grep -A 1 $PACKAGE_NAME

Dane wyjściowe powinny zawierać informację, że pakiet został skompilowany:

[com.example.app]
  path: /data/app/~~YvNxUxuP2e5xA6EGtM5i9A==/com.example.app-zQ0tkJN8tDrEZXTlrDUSBg==/base.apk
  arm64: [status=speed-profile] [reason=install-dm]

Teraz możesz mierzyć wydajność uruchamiania aplikacji tak jak wcześniej, ale bez resetowania stanu skompilowanego. Upewnij się, że nie resetujesz skompilowanego stanu pakietu.

# Force stop app
adb shell am force-stop $PACKAGE_NAME
# Measure app startup
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

Profile podstawowe i profgen

W tej sekcji opisujemy, co robi narzędzie profgen podczas tworzenia kompaktowej wersji binarnej profilu podstawowego.

Profgen-cli pomaga w kompilacji, introspekcji i transpilacji profili ART, dzięki czemu można je instalować na urządzeniach z Androidem niezależnie od docelowej wersji pakietu SDK.

Profgen-cli to interfejs wiersza poleceń, który kompiluje HRF profilu podstawowego do formatu skompilowanego. Interfejs wiersza poleceń jest też dostępny w repozytorium cmdline-tools jako część pakietu Android SDK.

Te funkcje są dostępne w gałęzi studio-main:

➜ ../cmdline-tools/latest/bin
apkanalyzer
avdmanager
lint
profgen
retrace
screenshot2
sdkmanager

Tworzenie kompaktowych profili binarnych za pomocą Profgen-cli

Polecenia dostępne w Profgen-cli to bin, validate i dumpProfile. Aby zobaczyć dostępne polecenia, użyj profgen --help:

➜  profgen --help
Usage: profgen options_list
Subcommands:
    bin - Generate Binary Profile
    validate - Validate Profile
    dumpProfile - Dump a binary profile to a HRF

Options:
    --help, -h -> Usage info

Aby wygenerować kompaktowy profil binarny, użyj polecenia bin. Oto przykładowe wywołanie:

profgen bin ./baseline-prof.txt \
  --apk ./release.apk \
  --map ./obfuscation-map.txt \
  --profile-format v0_1_0_p \
  --output ./baseline.prof \

Aby zobaczyć dostępne opcje, użyj profgen bin options_list:

Usage: profgen bin options_list
Arguments:
    profile -> File path to Human Readable profile { String }
Options:
    --apk, -a -> File path to apk (always required) { String }
    --output, -o -> File path to generated binary profile (always required)
    --map, -m -> File path to name obfuscation map { String }
    --output-meta, -om -> File path to generated metadata output { String }
    --profile-format, -pf [V0_1_0_P] -> The ART profile format version
      { Value should be one of [
         v0_1_5_s, v0_1_0_p, v0_0_9_omr1, v0_0_5_o, v0_0_1_n
        ]
      }
    --help, -h -> Usage info

Pierwszy argument reprezentuje ścieżkę do pliku HRF baseline-prof.txt.

Profgen-cli wymaga też ścieżki do kompilacji do publikacji pliku APK i mapy zaciemniania, która służy do zaciemniania pliku APK podczas korzystania z R8 lub Proguard. Dzięki temu profgen może podczas tworzenia skompilowanego profilu tłumaczyć symbole źródłowe w HRF na odpowiadające im zaciemnione nazwy.

Formaty profilów ART nie są zgodne w przód ani wstecz, dlatego podaj format profilu, aby profgen spakował metadane profilu (profm), których możesz użyć do transkodowania jednego formatu profilu ART na inny, gdy będzie to konieczne.

Formaty profili i wersje platformy

Podczas wybierania formatu profilu dostępne są te opcje:

Format profilu Wersja platformy Poziom API
v0_1_5_s Android S+ ponad 31
v0_1_0_p Android P, Q i R 28-30
v0_0_9_omr1 Android O MR1 27
v0_0_5_o Android O 26
v0_0_1_n Android N 24-25

Skopiuj pliki wyjściowe baseline.prof i baseline.profm do folderu assets lub dexopt w pliku APK.

Mapy zaciemniania kodu

Mapę zaciemniania musisz podać tylko wtedy, gdy HRF używa symboli źródłowych. Jeśli plik HRF jest generowany z kompilacji do publikacji, która jest już zaciemniona, i nie jest wymagane mapowanie, możesz zignorować tę opcję i skopiować dane wyjściowe do folderu assets lub dexopt.

Tradycyjna instalacja profili podstawowych

Profile Baseline są zwykle dostarczane na urządzenie na 1 z 2 sposobów.

Używanie install-multiple z DexMetadata

Na urządzeniach z interfejsem API 28 lub nowszym klient Google Play pobiera plik APK i ładunek DexMetadata (DM) dla instalowanej wersji pliku APK. DM zawiera informacje o profilu, które są przekazywane do Menedżera pakietów na urządzeniu.

Plik APK i DM są instalowane w ramach jednej sesji instalacji, np. za pomocą:

adb install-multiple base.apk base.dm

Jetpack ProfileInstaller

Na urządzeniach z interfejsem API na poziomie 29 lub wyższym biblioteka Jetpack ProfileInstaller udostępnia alternatywny mechanizm instalowania profilu spakowanego w pliku assets lub dexopt po zainstalowaniu pliku APK na urządzeniu. ProfileInstaller jest wywoływany przez ProfileInstallReceiver lub bezpośrednio przez aplikację.

Biblioteka ProfileInstaller transkoduje profil na podstawie wersji pakietu SDK urządzenia docelowego i kopiuje go do katalogu cur na urządzeniu (katalogu przejściowego dla profili ART na urządzeniu, który jest powiązany z pakietem).

Gdy urządzenie jest nieaktywne, profil jest pobierany przez proces o nazwie bg-dexopt na urządzeniu.

Instalowanie profilu podstawowego z nieoficjalnych źródeł

W tej sekcji opisano, jak zainstalować profil Baseline w przypadku pakietu APK.

Transmisja z androidx.profileinstaller

Na urządzeniach z interfejsem API 24 lub nowszym możesz wysłać polecenie zainstalowania profilu:

# Broadcast the install profile command - moves binary profile from assets
#     to a location where ART uses it for the next compile.
#     When successful, the following command prints "1":
adb shell am broadcast \
    -a androidx.profileinstaller.action.INSTALL_PROFILE \
    <pkg>/androidx.profileinstaller.ProfileInstallReceiver

# Kill the process
am force-stop <pkg>

# Compile the package based on profile
adb shell cmd package compile -f -m speed-profile <pkg>

Biblioteka ProfileInstaller nie jest obecna w większości plików APK z profilami Baseline, które znajdują się w około 77 tys. z 450 tys. aplikacji w Google Play, ale jest obecna w praktycznie każdym pliku APK korzystającym z Compose. Dzieje się tak, ponieważ biblioteki mogą udostępniać profile bez deklarowania zależności od ProfileInstaller. Dodanie zależności w każdej bibliotece z profilem obowiązuje od Jetpacka.

Używanie install-multiple z profgen lub DexMetaData

Na urządzeniach z interfejsem API w wersji 28 lub nowszej możesz zainstalować profil Baseline z nieoficjalnego źródła bez konieczności posiadania biblioteki ProfileInstaller w aplikacji.

Aby to zrobić, użyj Profgen-cli:

profgen extractProfile \
        --apk app-release.apk \
        --output-dex-metadata app-release.dm \
        --profile-format V0_1_5_S # Select based on device and the preceding table.

# Install APK and the profile together
adb install-multiple appname-release.apk appname-release.dm

Aby obsługiwać podziały plików APK, wykonaj poprzednie kroki wyodrębniania profilu raz dla każdego pliku APK. Podczas instalacji przekaż każdy plik APK i powiązany z nim plik .dm, upewniając się, że nazwy plików APK i .dm są zgodne:

adb install-multiple appname-base.apk appname-base.dm \
appname-split1.apk appname-split1.dm

Weryfikacja

Aby sprawdzić, czy profil został prawidłowo zainstalowany, możesz wykonać czynności opisane w sekcji Ręczne mierzenie ulepszeń aplikacji.

Zrzut zawartości profilu binarnego

Aby sprawdzić zawartość kompaktowej wersji binarnej profilu Baseline, użyj opcji Profgen-cli dumpProfile:

Usage: profgen dumpProfile options_list
Options:
    --profile, -p -> File path to the binary profile (always required)
    --apk, -a -> File path to apk (always required) { String }
    --map, -m -> File path to name obfuscation map { String }
    --strict, -s [true] -> Strict mode
    --output, -o -> File path for the HRF (always required) { String }
    --help, -h -> Usage info

dumpProfile potrzebuje pliku APK, ponieważ kompaktowa reprezentacja binarna przechowuje tylko przesunięcia DEX, więc potrzebuje ich do rekonstrukcji nazw klas i metod.

Tryb ścisły jest domyślnie włączony i sprawdza zgodność profilu z plikami DEX w pakiecie APK. Jeśli próbujesz debugować profile wygenerowane przez inne narzędzie, mogą wystąpić błędy zgodności, które uniemożliwią zrzut do analizy. W takich przypadkach możesz wyłączyć tryb ścisły za pomocą --strict false. Jednak w większości przypadków tryb ścisły powinien być włączony.

Mapa zaciemniania jest opcjonalna. Jeśli ją podasz, pomoże ona ponownie zmapować zaciemnione symbole na ich wersje zrozumiałe dla człowieka, co ułatwi korzystanie z nich.