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ć reguły profilu i mierzyć je w aplikacji.

Ręczne definiowanie reguł profili

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, w którym znajduje się plik AndroidManifest.xml.

W pliku każda reguła jest podana w osobnym 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 czytelnego 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 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 czytelny dla człowieka (HRF), więc symbole wieloznaczne nie są obsługiwane.

Składnia reguły

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

[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]

Reguła klasy ma następujący wzór:

[CLASS_DESCRIPTOR]

Szczegółowy opis znajdziesz w tabeli poniżej:

Składnia Opis
FLAGS Reprezentuje jeden lub więcej znaków H, SP, aby wskazać, czy ta metoda musi być oznaczona jako Hot, Startup lub Post Startup w odniesieniu do 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 wywoływana podczas uruchamiania.

Metoda z flagą P oznacza, ż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 stercie, aby uniknąć kosztów ładowania klasy. Kompilator ART wykorzystuje różne strategie optymalizacji, takie jak kompilacja AOT tych metod i optymalizacja układu w wygenerowanym pliku AOT.
CLASS_DESCRIPTOR Deskryptor klasy kierowanej metody. Na przykład androidx.compose.runtime.SlotTable ma deskryptor Landroidx/compose/runtime/SlotTable;. Przedrostek L jest dodawany zgodnie z formatem pliku wykonywalnego Dalvik (DEX).
METHOD_SIGNATURE Sygnatura metody, w tym nazwa, typy parametrów i typy zwracanych wartości. Na przykład:

// LayoutNode.kt

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

na LayoutNode ma podpis isPlaced()Z.

Wzorce te mogą zawierać symbole wieloznaczne, dzięki czemu jedna reguła może obejmować wiele metod lub klas. Aby uzyskać pomoc podczas 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 podstawowego obsługują te typy: Więcej informacji o tych typach znajdziesz w artykule Format pliku wykonywalnego Dalvik (DEX).

Znak Typ Opis
B bajt Bajt ze znakiem
C char Punkt kodowy znaku Unicode zakodowany w UTF-16
D podwójny 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 film Short
V pusty, Anuluj
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 artefakty AAR. Gdy tworzysz plik APK, aby uwzględnić te artefakty, reguły są łączone ze sobą – podobnie jak w przypadku łączenia plików manifestu – 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 kompilacji AOT określonego podzbioru aplikacji w momencie instalacji na Androidzie 9 (poziom interfejsu API 28) lub Androidzie 7 (poziom interfejsu API 24) w przypadku korzystania z ProfileInstaller.

Ręczne zbieranie profili podstawowych

Możesz ręcznie wygenerować profil podstawowy bez konfigurowania biblioteki Macrobenchmark i utworzyć automatyzację interfejsu użytkownika w przypadku najważniejszych ścieżek użytkownika. Zalecamy używanie 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 Gradle profilu podstawowego. W takich przypadkach możesz ręcznie zebrać reguły profilu podstawowego. Jest to znacznie łatwiejsze, jeśli używasz urządzenia lub emulatora z 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ę aplikacji na urządzeniu testowym. Typ kompilacji aplikacji nie może być zoptymalizowany pod kątem R8 ani nie może być debugowany, 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 Twój 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 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 na poziomie 33 lub niższym

    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 Instalator profili Jetpack, 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 instalatora profili, ręcznie wyeksportuj profile na 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. Przekształć 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 na poziomie 33 lub niższym

    Sprawdź, czy utworzono 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

    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

  8. Użyj polecenia 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 podstawowy. Aby to sprawdzić, wykonaj czynności opisane w sekcji Problemy z instalacją.

Ręczne pomiary ulepszeń aplikacji

Zdecydowanie zalecamy mierzenie ulepszeń aplikacji za pomocą testów porównawczych. Jeśli jednak chcesz mierzyć postępy ręcznie, możesz zacząć od pomiaru nieoptymalizowanego uruchamiania aplikacji w celu uzyskania wartości 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 wgraj 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 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 plik HRF profilu podstawowego do skompilowanego formatu. Interfejs wiersza poleceń jest też dostępny w repozytorium cmdline-tools w ramach 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, 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ć 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 to ścieżka do pliku baseline-prof.txt HRF.

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

Formaty profili ART nie są kompatybilne wstecz ani w przód, dlatego podaj format profilu, aby pakiet profgen zawierał metadane profilu (profm), których możesz użyć do transkodowania jednego formatu profilu ART na inny w razie potrzeby.

Formaty profili i wersje platform

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 zaciemniania

Mapę zaciemniania musisz przesłać tylko wtedy, gdy plik HRF używa symboli źródłowych. Jeśli plik HRF jest generowany na podstawie wersji, która jest już zaciemniona i nie wymaga mapowania, możesz zignorować tę opcję i skopiować dane wyjściowe do folderu assets lub dexopt.

Tradycyjna instalacja profili podstawowych

Profile podstawowe są zwykle dostarczane na urządzenie na jeden z 2 sposobów.

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 APK. DM zawiera informacje o profilu, które są przekazywane do Menedżera pakietów na urządzeniu.

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

adb install-multiple base.apk base.dm

Jetpack ProfileInstaller

Na urządzeniach z interfejsem API na poziomie 29 lub nowszym biblioteka Jetpack ProfileInstaller zapewnia alternatywny mechanizm instalowania profilu spakowanego w assetslub dexopt po zainstalowaniu pliku APK na urządzeniu. ProfileInstaller jest wywoływana 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 specyficznego dla pakietu na potrzeby profili ART na urządzeniu).

Gdy urządzenie jest nieaktywne, 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łaj za pomocą: androidx.profileinstaller

Na urządzeniach z API w wersji 24 lub nowszej możesz rozgłosić 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>

Instalator profili nie występuje w większości plików APK z profilami podstawowymi (czyli w około 77 tys. z 450 tys. aplikacji w Google Play), ale jest obecny w praktycznie każdym pliku APK korzystającym z Compose. Wynika to z faktu, że 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 Jetpacka.

Używanie install-multiple z profgen lub DexMetaData

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

W tym celu 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ć podzielone pliki APK, wykonaj powyższe czynności związane z wyodrębnianiem 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 pliku APK i pliku .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 artykule Ręczne pomiary ulepszeń aplikacji.

Zrzucanie zawartości profilu binarnego

Aby sprawdzić zawartość skompaktowanej wersji binarnej profilu podstawowego, 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, a więc potrzebuje ich do odtworzenia 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ą zrzucenie danych do analizy. W takich przypadkach możesz wyłączyć tryb ścisły za pomocą ikony --strict false. W większości przypadków tryb ścisły powinien być jednak włączony.

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 z nich.