Basisprofile manuell erstellen und messen

Wir empfehlen dringend, die Generierung von Profilregeln mit der Jetpack Macrobenchmark-Bibliothek zu automatisieren, um den manuellen Aufwand zu reduzieren und die allgemeine Skalierbarkeit zu erhöhen. Es ist jedoch möglich, Profilregeln in Ihrer App manuell zu erstellen und zu messen.

Profilregeln manuell definieren

Sie können Profilregeln manuell in einer App oder einem Bibliotheksmodul definieren, indem Sie eine Datei mit dem Namen baseline-prof.txt im Verzeichnis src/main erstellen. Das ist derselbe Ordner, der die Datei AndroidManifest.xml enthält.

Die Datei enthält eine Regel pro Zeile. Jede Regel stellt ein Muster für den Abgleich von Methoden oder Klassen in der App oder Bibliothek dar, die optimiert werden müssen.

Die Syntax für diese Regeln ist eine Obermenge des menschenlesbaren ART-Profilformats (Human-Readable Format, HRF), wenn adb shell profman --dump-classes-and-methods verwendet wird. Die Syntax ähnelt der Syntax für Deskriptoren und Signaturen, ermöglicht aber die Verwendung von Platzhaltern, um das Schreiben von Regeln zu vereinfachen.

Das folgende Beispiel zeigt einige Baseline Profile-Regeln, die in der Jetpack Compose-Bibliothek enthalten sind:

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;

Sie können versuchen, Profilregeln in diesem Compiler Explorer-Beispielprojekt zu ändern. Compiler Explorer unterstützt nur das für Menschen lesbare ART-Profilformat (Human-Readable Format, HRF). Platzhalter werden daher nicht unterstützt.

Regelsyntax

Diese Regeln können entweder auf Methoden oder auf Klassen ausgerichtet sein:

[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]

Für eine Klassenregel gilt das folgende Muster:

[CLASS_DESCRIPTOR]

Eine detaillierte Beschreibung finden Sie in der folgenden Tabelle:

Syntax Beschreibung
FLAGS Stellt ein oder mehrere der Zeichen H, S und P dar, um anzugeben, ob diese Methode in Bezug auf den Starttyp als Hot, Startup oder Post Startup gekennzeichnet werden muss.

Eine Methode mit dem Flag H ist eine „heiße“ Methode, d. h., sie wird während der Lebensdauer der App viele Male aufgerufen.

Eine Methode mit dem Flag S ist eine Methode, die beim Start aufgerufen wird.

Eine Methode mit dem Flag P wird nach dem Start aufgerufen.

Eine in dieser Datei vorhandene Klasse weist darauf hin, dass sie beim Start verwendet wird und im Heap vorab zugewiesen werden muss, um die Kosten für das Laden der Klasse zu vermeiden. Der ART-Compiler verwendet verschiedene Optimierungsstrategien, z. B. die AOT-Kompilierung dieser Methoden und die Durchführung von Layoutoptimierungen in der generierten AOT-Datei.
CLASS_DESCRIPTOR Deskriptor für die Klasse der Zielmethode. androidx.compose.runtime.SlotTable hat beispielsweise den Deskriptor Landroidx/compose/runtime/SlotTable;. Gemäß dem Dalvik Executable (DEX)-Format wird hier ein „L“ vorangestellt.
METHOD_SIGNATURE Signatur der Methode, einschließlich Name, Parametertypen und Rückgabetypen der Methode. Beispiel:

// LayoutNode.kt

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

on LayoutNode hat die Signatur isPlaced()Z.

Diese Muster können Platzhalter enthalten, damit eine einzelne Regel mehrere Methoden oder Klassen umfasst. Wenn Sie in Android Studio Unterstützung beim Schreiben von Regeln in der entsprechenden Syntax benötigen, können Sie das Android Baseline Profiles-Plug-in verwenden.

Eine Beispielregel mit Platzhaltern könnte so aussehen:

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

Unterstützte Typen in Baseline-Profilregeln

Baseline Profile-Regeln unterstützen die folgenden Typen. Weitere Informationen zu diesen Typen finden Sie unter Dalvik Executable (DEX).

Zeichen Eingeben Beschreibung
B Byte Signiertes Byte
C char Unicode-Zeichen-Codepoint, codiert in UTF-16
D Doppelt Gleitkommawert mit doppelter Genauigkeit
F float Gleitkommawert mit einfacher Genauigkeit
I int Ganzzahl
J long Lange Ganzzahl
S kurz Signiertes Short
V void Stornieren
Z Boolesch Richtig oder falsch?
L (Klassenname) Referenz Eine Instanz eines Klassennamens

Außerdem können in Bibliotheken Regeln definiert werden, die in AAR-Artefakten enthalten sind. Wenn Sie ein APK erstellen, das diese Artefakte enthält, werden die Regeln zusammengeführt – ähnlich wie beim Zusammenführen von Manifesten – und in ein kompaktes binäres ART-Profil kompiliert, das spezifisch für das APK ist.

ART verwendet dieses Profil, wenn das APK auf Geräten verwendet wird, um bei der Installation unter Android 9 (API-Level 28) oder Android 7 (API-Level 24) mit ProfileInstaller eine bestimmte Teilmenge der App AOT-zu kompilieren.

Baseline-Profile manuell erfassen

Sie können ein Baseline-Profil manuell generieren, ohne die Macrobenchmark-Bibliothek einzurichten, und UI-Automatisierungen Ihrer kritischen User Journeys erstellen. Wir empfehlen zwar, Macrobenchmarks zu verwenden, aber das ist möglicherweise nicht immer möglich. Wenn Sie beispielsweise ein Build-System verwenden, das nicht auf Gradle basiert, können Sie das Baseline Profile Gradle-Plug-in nicht verwenden. In solchen Fällen können Sie Baseline Profile-Regeln manuell erfassen. Das ist viel einfacher, wenn Sie ein Gerät oder einen Emulator mit API 34 und höher verwenden. Das ist zwar auch mit niedrigeren API-Levels möglich, erfordert aber Root-Zugriff und die Verwendung eines Emulators, auf dem ein AOSP-Image ausgeführt wird. Sie können Regeln direkt so erfassen:

  1. Installieren Sie eine Releaseversion Ihrer App auf einem Testgerät. Der App-Build-Typ darf nicht R8-optimiert sein und darf nicht debugfähig sein, damit ein Profil erfasst werden kann, das vom Build-System verwendet werden kann.
  2. Deaktivieren Sie die Profilinstallation und beenden Sie die App.

    Wenn Ihr APK von der Jetpack-Bibliothek Profile Installer abhängig ist, wird beim ersten Start Ihres APK ein Profil erstellt. Dies kann den Prozess der Profilerstellung beeinträchtigen. Deaktivieren Sie die Funktion daher mit dem folgenden Befehl:

    adb shell am broadcast -a androidx.profileinstaller.action.SKIP_FILE WRITE_SKIP_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
  3. Setzen Sie die App-Kompilierung zurück und löschen Sie alle Profile.

    API 34 und höher

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

    API 33 und niedriger

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

  4. Führen Sie die App aus und durchlaufen Sie manuell die kritischen User Journeys, für die Sie ein Profil erfassen möchten.

  5. Warten Sie mindestens fünf Sekunden, damit sich die Profile stabilisieren können.

  6. Führen Sie die Speicheraktion aus und warten Sie, bis das Speichern abgeschlossen ist. Wenn Ihr APK von der Jetpack Profile Installer-Bibliothek abhängig ist, verwenden Sie diese, um die Profile zu exportieren:

    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
    Wenn Sie Profile Installer nicht verwenden, sichern Sie die Profile manuell auf einem Emulator mit dem folgenden Befehl:

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

  7. Konvertieren Sie die generierten binären Profile in Text:

    API 34 und höher

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

    API 33 und niedriger

    Prüfen Sie, ob ein Referenzprofil oder ein aktuelles Profil erstellt wurde. Ein Referenzprofil befindet sich an folgendem Speicherort:

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

    Ein aktuelles Profil befindet sich unter folgendem Pfad:

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

    Speicherort der APK ermitteln:

    adb root
    adb shell pm path $PACKAGE_NAME

    Conversion durchführen:

    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. Verwenden Sie adb, um das gesicherte Profil vom Gerät abzurufen:

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

Dadurch werden die generierten Profilregeln abgerufen und in Ihrem App-Modul installiert. Beim nächsten Build der App ist das Baseline-Profil enthalten. Prüfen Sie dies anhand der Schritte unter Installationsprobleme.

App-Verbesserungen manuell messen

Wir empfehlen dringend, Verbesserungen an der App durch Benchmarking zu messen. Wenn Sie Verbesserungen jedoch manuell messen möchten, können Sie damit beginnen, den unoptimierten App-Start als Referenz zu messen.

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"

Als Nächstes laden Sie das Baseline-Profil per Sideloading.

# 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

Führen Sie den folgenden Befehl aus, um zu prüfen, ob das Paket bei der Installation optimiert wurde:

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

In der Ausgabe muss angegeben werden, dass das Paket kompiliert wurde:

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

Sie können die App-Startleistung jetzt wie gewohnt messen, ohne den kompilierten Status zurückzusetzen. Achten Sie darauf, dass Sie den kompilierten Status für das Paket nicht zurücksetzen.

# 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"

Baseline-Profile und profgen

In diesem Abschnitt wird beschrieben, was das Tool profgen beim Erstellen einer kompakten binären Version eines Baseline-Profils macht.

Profgen-cli unterstützt die Profilkompilierung, die Introspektion und das Transpilieren von ART-Profilen, damit sie unabhängig von der Ziel-SDK-Version auf Android-Geräten installiert werden können.

Profgen-cli ist eine CLI, die das HRF eines Baseline-Profils in das kompilierte Format kompiliert. Die Befehlszeile ist auch im cmdline-tools-Repository als Teil des Android SDK enthalten.

Die folgenden Funktionen sind im studio-main-Branch verfügbar:

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

Kompakte binäre Profile mit Profgen-cli erstellen

Die mit Profgen-cli verfügbaren Befehle sind bin, validate und dumpProfile. Verwenden Sie profgen --help, um die verfügbaren Befehle aufzurufen:

  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

Verwenden Sie den Befehl bin, um das kompakte binäre Profil zu generieren. Hier ein Beispiel für einen Aufruf:

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

Verwenden Sie profgen bin options_list, um die verfügbaren Optionen aufzurufen:

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

Das erste Argument stellt den Pfad zur baseline-prof.txt-HRF dar.

Für profgen-cli ist außerdem der Pfad zum Release-Build des APK und eine Verschleierungszuordnung erforderlich, die zum Verschleiern des APK verwendet wird, wenn R8 oder Proguard verwendet wird. So kann profgen Quellsymbole im HRF beim Erstellen des kompilierten Profils in die entsprechenden verschleierten Namen übersetzen.

Da ART-Profilformate nicht vorwärts- oder rückwärtskompatibel sind, müssen Sie ein Profilformat angeben, damit profgen Profilmetadaten (profm) verpackt, die Sie bei Bedarf zum Transcodieren eines ART-Profilformats in ein anderes verwenden können.

Profilformate und Plattformversionen

Folgende Optionen stehen zur Verfügung, wenn Sie ein Profilformat auswählen:

Profilformat Plattform-Version API-Ebene
v0_1_5_s Android S oder höher 31+
v0_1_0_p Android P, Q und 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

Kopieren Sie die Ausgabedateien baseline.prof und baseline.profm in den Ordner assets oder dexopt in der APK.

Verschleierungskarten

Sie müssen die Verschleierungstabelle nur angeben, wenn im HRF Quellsymbole verwendet werden. Wenn die HRF aus einem Release-Build generiert wird, der bereits verschleiert ist und keine Zuordnung erforderlich ist, können Sie diese Option ignorieren und die Ausgaben in den Ordner assets oder dexopt kopieren.

Traditionelle Installation von Baseline-Profilen

Baseline-Profile werden normalerweise auf zwei Arten auf ein Gerät übertragen.

install-multiple mit DexMetadata verwenden

Auf Geräten mit API 28 und höher lädt der Play-Client das APK und die DexMetadata-Nutzlast (DM) für eine APK-Version herunter, die installiert wird. Der DM enthält die Profilinformationen, die auf dem Gerät an den Paketmanager übergeben werden.

Das APK und das Geräte-Modell werden im Rahmen einer einzelnen Installationssitzung installiert, z. B. mit folgendem Befehl:

adb install-multiple base.apk base.dm

Jetpack ProfileInstaller

Auf Geräten mit API-Level 29 und höher bietet die Jetpack ProfileInstaller-Bibliothek einen alternativen Mechanismus zum Installieren eines Profils, das in assetsoder dexopt verpackt ist, nachdem das APK auf dem Gerät installiert wurde. ProfileInstaller wird von ProfileInstallReceiver oder direkt von der App aufgerufen.

Die ProfileInstaller-Bibliothek transkodiert das Profil basierend auf der SDK-Version des Zielgeräts und kopiert das Profil in das Verzeichnis cur auf dem Gerät (ein paketbezogenes Staging-Verzeichnis für ART-Profile auf dem Gerät).

Sobald das Gerät inaktiv ist, wird das Profil von einem Prozess namens bg-dexopt auf dem Gerät abgerufen.

Baseline-Profil querladen

In diesem Abschnitt wird beschrieben, wie Sie ein Baseline-Profil anhand einer APK-Datei installieren.

Mit androidx.profileinstaller übertragen

Auf Geräten mit API 24 und höher können Sie einen Broadcast-Befehl senden, um das Profil zu installieren:

# 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>

ProfileInstaller ist in den meisten APKs mit Baseline Profiles nicht vorhanden. Das betrifft etwa 77.000 von 450.000 Apps bei Google Play. Allerdings ist ProfileInstaller in praktisch jedem APK vorhanden, in dem Compose verwendet wird. Das liegt daran, dass Bibliotheken Profile bereitstellen können, ohne eine Abhängigkeit von ProfileInstaller zu deklarieren. Ab Jetpack wird in jeder Bibliothek mit einem Profil eine Abhängigkeit hinzugefügt.

install-multiple mit profgen oder DexMetaData verwenden

Auf Geräten mit API 28 und höher können Sie ein Baseline-Profil per Sideloading installieren, ohne dass die ProfileInstaller-Bibliothek in der App vorhanden sein muss.

Verwenden Sie dazu 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

Wenn Sie APK-Splits unterstützen möchten, führen Sie die vorherigen Schritte zum Extrahieren des Profils einmal pro APK aus. Übergeben Sie bei der Installation jedes APK und die zugehörige .dm-Datei. Achten Sie darauf, dass die Namen des APK und der .dm-Datei übereinstimmen:

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

Bestätigung

So prüfen Sie, ob das Profil richtig installiert ist: App-Verbesserungen manuell messen

Inhalte eines binären Profils ausgeben

Wenn Sie den Inhalt einer kompakten binären Version eines Baseline-Profils untersuchen möchten, verwenden Sie die Option dumpProfile der 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 benötigt das APK, da in der kompakten binären Darstellung nur DEX-Offsets gespeichert werden. Daher sind sie erforderlich, um Klassen- und Methodennamen zu rekonstruieren.

Der strenge Modus ist standardmäßig aktiviert. Dabei wird ein Kompatibilitätscheck des Profils mit den DEX-Dateien im APK durchgeführt. Wenn Sie versuchen, Profile zu debuggen, die von einem anderen Tool generiert wurden, können Kompatibilitätsfehler auftreten, die verhindern, dass Sie die Profile zur Untersuchung sichern können. In solchen Fällen können Sie den strengen Modus mit --strict false deaktivieren. In den meisten Fällen sollten Sie den strengen Modus jedoch aktiviert lassen.

Eine Verschleierungszuordnung ist optional. Wenn sie angegeben wird, hilft sie, verschleierte Symbole zur besseren Lesbarkeit neu zuzuordnen.