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

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

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 1 regułę na wiersz. Każda reguła reprezentuje wzór dopasowywania metod lub klas w aplikacji lub bibliotece, który wymaga optymalizacji.

Składnia tych reguł jest superzestawem czytelnego dla człowieka formatu profilu ART (HRF) w przypadku użycia adb shell profman --dump-classes-and-methods. Składnia jest podobna do składni stosowanej w przypadku deskryptorów i podpisów, ale umożliwia używanie symboli wieloznacznych w celu uproszczenia procesu tworzenia reguł.

Ten przykład pokazuje kilka reguł profilu podstawowego dostępnych w bibliotece Compose w Jetpacku:

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 próbnym projekcie Compiler Explorer. Pamiętaj, że Compiler Explorer obsługuje tylko format profilu ART zrozumiały dla człowieka (HRF), więc znaki zastępcze nie są obsługiwane.

Składnia reguł

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

[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]

Reguła klasy używa tego wzoru:

[CLASS_DESCRIPTOR]

Szczegółowe informacje znajdziesz w tabeli poniżej:

Składnia Opis
FLAGS Reprezentuje co najmniej jeden z znaków H, SP, aby wskazać, czy ta metoda musi być oznaczona jako Hot, Startup czy Post Startup w zależności od typu uruchamiania.

Metoda z flagą H oznacza, że jest to „gorąca” metoda, czyli jest wywoływana wiele razy w trakcie działania aplikacji.

Metoda z flagą S oznacza, ż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 być wstępnie przydzielona w stogu, 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 docelowej metody. Na przykład androidx.compose.runtime.SlotTable ma deskryptor Landroidx/compose/runtime/SlotTable;. L jest dodawane na początku zgodnie z formatem Dalvik Executable (DEX).
METHOD_SIGNATURE sygnaturę metody, w tym nazwę, typy parametrów i typy zwracane metody; Na przykład:

// LayoutNode.kt

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

na LayoutNode ma podpis isPlaced()Z.

Te wzorce mogą zawierać symbole wieloznaczne, aby jedna reguła obejmowała wiele metod lub klas. Aby uzyskać pomoc podczas pisania kodu z syntaksą reguł w Android Studio, skorzystaj z wtyczki Android Baseline Profiles.

Przykład reguły z symbolem wieloznaczeniowym może wyglądać tak:

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

Obsługiwane typy w regułach profilu podstawowego

Reguły profilu podstawowego obsługują te typy. Szczegółowe informacje o tych typach znajdziesz w artykule Format pliku wykonywalnego Dalvik (DEX).

Znak Typ Opis
B bajt Bajt podpisany
C char Punkt kodowy znaku Unicode zakodowany w UTF-16
D podwójny Wartość zmiennoprzecinkowa podwójnej precyzji
F float Wartość zmiennoprzecinkowa o pojedynczej precyzji
I int Liczba całkowita
J długi Długa liczba całkowita
S krótki Podpisany film Short
V nieważne Anulowanie
Z wartość logiczna Prawda czy fałsz?
L (nazwa klasy) pliku referencyjnego instancja nazwy klasy,

Biblioteki mogą też definiować reguły, które są pakowane w elementy AAR. Gdy kompilujesz plik APK, aby zawierał te elementy, reguły są scalane (podobnie jak w przypadku scalania manifestu) i skompilowane w kompaktowy binarny profil ART, który jest specyficzny dla pliku APK.

Profil ten jest używany przez ART w przypadku plików APK na urządzeniach do kompilacji AOT określonego podzbioru aplikacji w momencie instalacji na Androidzie 9 (poziom interfejsu API 28) lub Androidzie 7 (poziom interfejsu API 24) podczas korzystania z ProfileInstaller.

Ręczne zbieranie profili podstawowych

Możesz ręcznie wygenerować profil referencyjny bez konfigurowania biblioteki Macrobenchmark i utworzyć automatyzacje interfejsu użytkownika dotyczące kluczowych ścieżek użytkownika. Chociaż zalecamy korzystanie z testów porównawczych makro, nie zawsze jest to możliwe. Jeśli na przykład używasz systemu kompilacji innego niż Gradle, nie możesz używać wtyczki Gradle do profilu bazowego. W takich przypadkach możesz ręcznie zebrać zasady dotyczące profilu podstawowego. Jest to znacznie łatwiejsze, jeśli używasz urządzenia lub emulatora z poziomem interfejsu API 34 lub nowszym. Chociaż jest to nadal możliwe na niższych poziomach interfejsu API, wymaga dostępu roota i użycia emulatora z obrazem AOSP. Aby zebrać reguły bezpośrednio:

  1. Zainstaluj wersję aplikacji na urządzeniu testowym. Aby uzyskać dokładny profil, typ kompilacji aplikacji musi być zoptymalizowany pod kątem R8 i niemożliwy do debugowania.
  2. Upewnij się, że profile nie zostały już skompilowane.

    Interfejs API 34 lub nowszy

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

    Poziom 33 interfejsu API lub niższy

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

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

    adb shell am broadcast -a androidx.profileinstaller.action.SKIP_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
  3. Uruchom aplikację i ręcznie przejdź przez najważniejsze ścieżki użytkownika, których profil chcesz stworzyć.
  4. Poproś ART o wygenerowanie profili. Jeśli plik APK jest zależny od biblioteki Profile Installer z Jetpacka, użyj jej do zrzutu profili:

    adb shell am broadcast -a androidx.profileinstaller.action.SAVE_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
    adb shell am force-stop $PACKAGE_NAME
    Jeśli nie używasz Profile Installer, wygeneruj profile ręcznie na emulatorze za pomocą tego polecenia:

    adb root
    adb shell killall -s SIGUSR1 $PACKAGE_NAME
    adb shell am force-stop $PACKAGE_NAME
  5. Zaczekaj co najmniej 5 sekund, aby umożliwić wygenerowanie profilu.
  6. Konwertuj wygenerowane profile binarne na tekst:

    Interfejs API 34 lub nowszy

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

    Poziom 33 interfejsu API lub niższy

    Określ, czy został utworzony profil referencyjny czy bieżący. Profil referencyjny znajduje się w tym miejscu:

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

    Bieżący profil znajduje się w tym miejscu:

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

    Określ lokalizację pliku APK:

    adb root
    adb shell pm path $PACKAGE_NAME

    Przeprowadź 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

  7. Aby pobrać z urządzenia wygenerowany profil, użyj polecenia adb:

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

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

Ręczne pomiary ulepszeń aplikacji

Zdecydowanie zalecamy pomiar ulepszeń aplikacji za pomocą porównań. Jeśli jednak chcesz mierzyć ulepszenia ręcznie, możesz zacząć od pomiaru czasu uruchamiania nieoptymalizowanej aplikacji.

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 prześlij profil podstawowy.

.
# 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 muszą zawierać informację o skompilowaniu pakietu:

[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 skompilowanego stanu. Upewnij się, że nie zresetujesz 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 opisano, co robi narzędzie profgen podczas kompilowania skompresowanej wersji binarnej profilu bazowego.

Narzędzie Profgen-cli ułatwia kompilowanie profili, analizowanie ich i transpilowanie profili ART, aby można je było 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 skompilowanego formatu. Interfejs wiersza poleceń jest też dostępny w repozytorium cmdline-tools w ramach pakietu SDK Androida.

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

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

Tworzenie skompresowanych profili binarnych za pomocą narzędzia Profgen-cli

Polecenia dostępne w programie Profgen-cli to bin, validatedumpProfile. 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ć skompresowany profil binarny, użyj polecenia bin. Oto przykład wywołania:

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 baseline-prof.txt HRF.

Narzędzie profgen-cli wymaga też ścieżki do wersji APK przeznaczonej do wydania oraz mapy zaciemnienia, która służy do zaciemnienia pliku APK podczas korzystania z R8 lub Proguard. Dzięki temu podczas tworzenia skompilowanego profilu profgen może przekształcać symbole źródłowe w HRF w odpowiednie zaciemnione nazwy.

Formaty profili ART nie są kompatybilne wstecz ani wprzód, dlatego podaj format profilu, aby profgen mogło przechowywać metadane profilu (profm), których możesz używać do konwersji jednego formatu profilu ART na inny, gdy zajdzie taka potrzeba.

Formaty profilu i wersje platformy

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

Format profilu Wersja platformy Poziom interfejsu 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.profbaseline.profm do folderu assets lub dexopt w pliku APK.

Mapy zaciemnienia

Mapę zaciemnienia musisz przesłać tylko wtedy, gdy plik HRF używa symboli źródłowych. Jeśli HRF jest generowany z wersji wydania, 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 podstawowe są tradycyjnie dostarczane na urządzenie na jeden z 2 sposobów.

Używanie install-multiple z DexMetadata

Na urządzeniach z poziomem interfejsu API 28 lub nowszym klient Google Play pobiera plik APK oraz dane 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 pojedynczej sesji instalacji za pomocą:

adb install-multiple base.apk base.dm

Jetpack ProfileInstaller

Na urządzeniach z poziomem interfejsu API 29 lub nowszym biblioteka Jetpack ProfileInstaller zapewnia alternatywny mechanizm instalowania profilu spakowanego w plik assets lub dexopt po zainstalowaniu pliku APK na urządzeniu. ProfileInstaller jest wywoływany przez ProfileInstallReceiver lub bezpośrednio przez aplikację.

Biblioteka ProfileInstaller konwertuje profil na podstawie wersji SDK na urządzeniu docelowym i kopiuje go do katalogu cur na urządzeniu (katalog pośredni dla profili ART na urządzeniu).

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

Przenoszenie profilu podstawowego

Z tej sekcji dowiesz się, jak zainstalować profil referencyjny za pomocą pliku APK.

Przesyłanie z urządzenia androidx.profileinstaller

Na urządzeniach z interfejsem API 24 lub nowszym możesz wysłać polecenie do 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>

Narzędzie ProfileInstaller nie występuje w większości plików APK z profilami podstawowymi (które są dostępne w około 77 tys. z 450 tys. aplikacji w Google Play), ale jest obecne w każdym pliku APK korzystającym z Compose. Dzieje się tak, ponieważ biblioteki mogą udostępniać profile bez deklarowania zależności od ProfileInstaller. Dodawanie zależności w każdej bibliotece z profilem jest możliwe od wersji Jetpacka.

Używanie install-multiple z profgen lub DexMetaData

Na urządzeniach z interfejsem API 28 lub nowszym możesz załadować profil podstawowy bez konieczności umieszczania w aplikacji biblioteki ProfileInstaller.

Aby to zrobić, użyj narzędzia 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 umożliwić dzielenie plików APK, wykonaj poniższe czynności ekstrakcji profilu raz na plik APK. Podczas instalacji należy przekazać każdy plik APK i powiązany plik .dm, z tym że nazwy plików APK i .dm muszą być takie same:

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

Weryfikacja

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

Wyświetlanie zawartości profilu binarnego

Aby sprawdzić zawartość skompresowanej wersji binarnej profilu referencyjnego, 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 binarna reprezentacja przechowuje tylko przesunięcia DEX, a dlatego potrzebuje ich do odtworzenia nazw klas i metod.

Tryb ścisły jest domyślnie włączony i sprawdza zgodność profilu z plikami DEX w pliku APK. Jeśli próbujesz debugować profile wygenerowane przez inne narzędzie, możesz napotkać problemy ze zgodnością, które uniemożliwiają wygenerowanie pliku z danymi do analizy. W takich przypadkach możesz wyłączyć tryb ścisły za pomocą --strict false. W większości przypadków jednak należy pozostawić włączony tryb ścisły.

Mapa zaciemnienia jest opcjonalna. Jeśli zostanie podana, pomoże zmapować zaciemnione symbole na wersje zrozumiałe dla człowieka, aby ułatwić korzystanie z usługi.