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

Zdecydowanie zalecamy zautomatyzowanie 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 swojej 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 znajdujący się w katalogu src/main. Jest to ten sam folder, który zawiera plik AndroidManifest.xml.

Plik określa 1 regułę w 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łego dla człowieka (HRF) podczas używania polecenia 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 pisania reguł.

Ten przykład pokazuje kilka reguł profilu podstawowego 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łego dla człowieka (HRF), więc symbole wieloznaczne nie są obsługiwane.

Składnia reguł

Te reguły przyjmują jedną z 2 postaci, aby kierować na metody lub klasy:

[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]

Reguła klasy używa tego wzorca:

[CLASS_DESCRIPTOR]

Szczegółowy opis znajdziesz w tej tabeli:

Składnia Opis
FLAGS Reprezentuje co najmniej 1 znak: H, S lub P, aby wskazać, czy ta metoda musi być oznaczona jako Hot, Startup lub Post Startup w zależności od typu uruchomienia.

Metoda z flagą H jest metodą „gorącą”, co oznacza, że jest wywoływana wiele razy w trakcie działania aplikacji.

Metoda z flagą S jest metodą wywoływaną podczas uruchamiania.

Metoda z flagą P jest metodą wywoływaną po uruchomieniu.

Klasa obecna w tym pliku wskazuje, że jest używana podczas uruchamiania i musi być wstępnie przydzielona w stercie, aby uniknąć kosztów ładowania 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;. W tym przypadku przedrostek L jest dodawany zgodnie z formatem Dalvik Executable (DEX).
METHOD_SIGNATURE Sygnatura metody, w tym jej nazwa, typy parametrów i typy zwracane. Przykład:

// LayoutNode.kt

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

w przypadku LayoutNode ma sygnaturę isPlaced()Z.

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

Przykład reguły z symbolem wieloznacznym:

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

Obsługiwane typy w regułach profilu podstawowego

Reguły profilu podstawowego obsługują te typy. Więcej informacji o tych typach, znajdziesz w artykule Format Dalvik Executable (DEX) format.

Znak Typ Opis
B bajt Bajt ze znakiem
C char Punkt kodowy Unicode zakodowany w 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 Krótka liczba ze znakiem
V void Void
Z Wartość logiczna Prawda czy fałsz?
L (nazwa zajęć) odniesienie Instancja nazwy klasy

Biblioteki mogą też definiować reguły, które są spakowane w artefakty AAR. Gdy tworzysz plik APK, aby uwzględnić te artefakty, reguły są łączone – podobnie jak w przypadku łączenia manifestów – i kompilowane do kompaktowego binarnego profilu ART, który jest specyficzny dla pliku APK.

ART wykorzystuje ten profil, gdy plik APK jest używany na urządzeniach do kompilowania AOT określonego podzbioru aplikacji podczas instalacji na Androidzie 9 (poziom interfejsu API 28), lub Androidzie 7 (poziom interfejsu API 24) w przypadku używania ProfileInstaller.

Ręczne zbieranie profili podstawowych

Możesz ręcznie wygenerować profil podstawowy bez konfigurowania biblioteki Macrobenchmark i tworzyć automatyzacje interfejsu użytkownika w przypadku najważniejszych ścieżek użytkownika. Chociaż zalecamy używanie biblioteki Macrobenchmark, 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 profilu podstawowego. W takich przypadkach możesz ręcznie zbierać reguły profilu podstawowego. Jest to znacznie łatwiejsze, jeśli używasz urządzenia lub emulatora z interfejsem API w wersji 34 lub nowszej. Chociaż jest to nadal możliwe w przypadku niższych poziomów interfejsu API, wymaga dostępu do roota i użycia emulatora z obrazem AOSP. Reguły możesz zbierać bezpośrednio, wykonując te czynności:

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

    Jeśli plik APK jest zależny od biblioteki Jetpack Profile Installer, biblioteka uruchomi profil przy pierwszym uruchomieniu pliku APK. Może to zakłócić proces generowania profilu, więc wyłącz go 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 w wersji 34 lub nowszej

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

    Interfejs API w wersji 33 lub starszej

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

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

  5. Odczekaj co najmniej 5 sekund, aby profile się ustabilizowały.

  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 zrzucenia 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 Profile Installer, 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 w wersji 34 lub nowszej

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

    Interfejs API w wersji 33 lub starszej

    Sprawdź, czy został utworzony profil odniesienia czy bieżący. Profil odniesienia 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ć z urządzenia zrzucony profil:

    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ępnej kompilacji aplikacji zostanie uwzględniony profil podstawowy. Sprawdź to wykonując czynności opisane w sekcji Problemy z instalacją.

Ręczne mierzenie ulepszeń aplikacji

Zdecydowanie zalecamy mierzenie ulepszeń aplikacji za pomocą testów porównawczych. Jeśli jednak chcesz mierzyć ulepszenia ręcznie, możesz zacząć od pomiaru uruchomienia 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 wczytaj 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ą wskazywać, ż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 stanu skompilowanego 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 kompilowaniu, 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 jego skompilowanego formatu. 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

Kompilowanie 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 polecenia 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

Użyj polecenia bin, aby wygenerować kompaktowy profil binarny. 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 polecenia 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 potrzebuje też ścieżki do kompilacji do publikacji pliku APK i mapy zaciemniania, która służy do zaciemniania pliku APK podczas używania R8 lub Proguard. Dzięki temu profgen może tłumaczyć symbole źródłowe w HRF na odpowiadające im zaciemnione nazwy podczas tworzenia skompilowanego profilu.

Ponieważ formaty profili ART nie są kompatybilne wstecz ani w przód, podaj format profilu, aby profgen mógł spakować metadane profilu (profm), których możesz użyć do transkodowania jednego formatu profilu ART na inny, gdy jest to wymagane.

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 lub nowszy 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

Mapę zaciemniania musisz podać tylko wtedy, gdy HRF używa symboli źródłowych. Jeśli HRF jest generowany na podstawie 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 podstawowe są tradycyjnie dostarczane na urządzenie na 2 sposoby.

Używanie install-multiple z DexMetadata

Na urządzeniach z interfejsem API w wersji 28 lub nowszej klient 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 za pomocą polecenia takiego jak:

adb install-multiple base.apk base.dm

Jetpack ProfileInstaller

Na urządzeniach z interfejsem API w wersji 29 lub nowszej biblioteka Jetpack ProfileInstaller zapewnia alternatywny mechanizm instalowania profilu spakowanego w 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 profil do katalogu cur na urządzeniu (katalog tymczasowy specyficzny dla pakietu na potrzeby profili ART na urządzeniu).

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

Wczytywanie profilu podstawowego

W tej sekcji opisujemy, jak zainstalować profil podstawowy na podstawie pliku APK.

Przesyłanie za pomocą androidx.profileinstaller

Na urządzeniach z interfejsem API w wersji 24 lub nowszej możesz przesł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 podstawowymi (czyli w około 77 tys. z 450 tys. aplikacji w 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. Dodawanie zależności w każdej bibliotece z profilem jest stosowane od Jetpack.

Używanie install-multiple z profgen lub DexMetadata

Na urządzeniach z interfejsem API w wersji 28 lub nowszej możesz wczytać profil podstawowy bez konieczności posiadania w aplikacji biblioteki ProfileInstaller.

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 APK, wykonaj powyższe czynności wyodrębniania profilu raz na każdy plik APK. Podczas instalacji przekaż każdy plik APK i powiązany z nim plik .dm, upewniając się, że nazwy pliku 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 jest prawidłowo zainstalowany, możesz wykonać czynności opisane w sekcji Ręczne mierzenie ulepszeń aplikacji.

Zrzucanie zawartości profilu binarnego

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

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, dlatego potrzebuje ich do rekonstrukcji nazw klas i metod.

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

Mapa zaciemniania jest opcjonalna. Jeśli ją podasz, pomoże ona w ponownym mapowaniu zaciemnionych symboli na ich wersje zrozumiałe dla człowieka, co ułatwi korzystanie.