Profils de référence

Les profils de référence sont une liste des classes et des méthodes incluses dans un APK, utilisés par Android Runtime (ART) lors de l'installation d'une application afin de précompiler les chemins critiques dans le code machine. Il s'agit d'une forme d'optimisation guidée des profils (PGO) qui permet aux applications d'optimiser le démarrage, de réduire les à-coups et d'améliorer les performances pour les utilisateurs finaux.

Fonctionnement des profils de référence

Les règles de profil sont compilées dans un format binaire dans l'APK, dans assets/dexopt/baseline.prof.

Lors de l'installation de l'application, ART effectue une compilation anticipée (AOT) des méthodes dans le profil, ce qui permet de les exécuter plus rapidement. Si le profil contient des méthodes utilisées lors du lancement de l'application ou lors de l'affichage des frames, l'utilisateur bénéficie d'un temps de lancement plus court et/ou d'une diminution des à-coups.

Lors du développement de votre application ou de votre bibliothèque, pensez à définir des profils de référence pour couvrir des chemins "Hot" spécifiques lors des critical user journeys, pour lesquels le délai d'affichage ou la latence sont importants, tels que le démarrage, les transitions ou le défilement.

Les profils de référence sont ensuite envoyés directement aux utilisateurs (via Google Play) avec l'APK, comme illustré sur la figure 1 :

Figure 1. Ce schéma montre le workflow de profil de référence, de l'importation jusqu'à la livraison à l'utilisateur final, et la manière dont il est associé aux profils cloud.

Pourquoi utiliser des profils de référence ?

Le temps de démarrage est un composant essentiel pour améliorer l'engagement utilisateur envers votre application. Augmenter la vitesse et la réactivité d'une application entraîne une augmentation du nombre d'utilisateurs actifs par jour et un taux moyen de visites de retour plus élevé.

Les profils cloud optimisent également ces mêmes interactions, mais ne sont disponibles pour les utilisateurs qu'au moins un jour après la publication d'une mise à jour et ne sont pas compatibles avec les versions d'Android 7 (niveau 24 d'API) à Android 8 (niveau 26 d'API).

Comportement de la compilation selon la version d'Android

Les versions de la plateforme Android ont utilisé différentes approches de compilation, chacune offrant un compromis en termes de performances. Les profils de référence améliorent les méthodes de compilation précédentes en fournissant un profil pour toutes les installations.

Version d'Android Méthode de compilation Approche d'optimisation
Android 5 (niveau 21 d'API) à Android 6 (niveau 23 d'API) AOT complet L'ensemble de l'application est optimisé lors de l'installation, ce qui augmente les temps d'attente, l'utilisation de la RAM et de l'espace disque et le temps de chargement du code, qui peut lui-même accroître le temps de démarrage à froid.
Android 7 (niveau 24 d'API) à Android 8.1 (niveau 27 d'API) AOT partiel (profil de référence) Les profils de référence sont installés par androidx.profileinstaller lors de la première exécution, au moment où le module d'application définit cette dépendance. ART peut améliorer encore ce processus en ajoutant des règles de profil supplémentaires en cas d'utilisation de l'application et en les compilant lorsque l'appareil est inactif. Cette approche optimise l'espace disque et le temps de chargement du code sur le disque, réduisant ainsi le temps d'attente de l'application.
Android version 9 (niveau 28 d'API) ou supérieure AOT partiel (profil de référence + profil cloud) Le cas échéant, Play optimise les APK et les profils cloud à l'aide de profils de référence pendant les installations d'applications. Après l'installation, les profils ART sont importés dans Play et rassemblés, puis fournis aux utilisateurs sous forme de profils cloud au moment de l'installation/mise à jour de l'application.

Créer des profils de référence

Créer automatiquement des règles de profil à l'aide de BaselineProfileRule

En tant que développeur d'applications, vous pouvez générer automatiquement des profils pour chaque version d'une application à l'aide de la bibliothèque Jetpack Macrobenchmark.

Pour créer des profils de référence à l'aide de la bibliothèque Macrobenchmark :

  1. Ajoutez une dépendance à la bibliothèque ProfileInstaller dans l'élément build.gradle de votre application pour activer la compilation du profil de référence local et du Play Store. Il s'agit du seul moyen de télécharger indépendamment en local un profil de référence.

    dependencies {
         implementation("androidx.profileinstaller:profileinstaller:1.2.0-beta01")
    }
    
  2. Configurez un module Macrobenchmark dans votre projet Gradle.

  3. Définissez un nouveau test appelé BaselineProfileGenerator qui ressemble à ceci :

    @ExperimentalBaselineProfilesApi
    @RunWith(AndroidJUnit4::class)
    class BaselineProfileGenerator {
        @get:Rule val baselineProfileRule = BaselineProfileRule()
    
        @Test
        fun startup() =
            baselineProfileRule.collectBaselineProfile(packageName = "com.example.app") {
                pressHome()
                // This block defines the app's critical user journey. Here we are interested in
                // optimizing for app startup. But you can also navigate and scroll
                // through your most important UI.
                startActivityAndWait()
            }
    }
    
  4. Connectez un élément userdebug ou un émulateur de projet Android Open Source (AOSP) sous Android version 9 ou supérieure.

  5. Exécutez la commande adb root à partir du terminal pour vous assurer que le daemon ADB s'exécute avec des autorisations racines.

  6. Exécutez le test et attendez qu'il se termine.

  7. Recherchez l'emplacement du profil généré dans logcat. Recherchez la balise de journal Benchmark.

    com.example.app D/Benchmark: Usable output directory: /storage/emulated/0/Android/media/com.example.app
    
    # List the output baseline profile
    ls /storage/emulated/0/Android/media/com.example.app
    SampleStartupBenchmark_startup-baseline-prof.txt
    
  8. Extrayez le fichier généré à partir de votre appareil.

    adb pull storage/emulated/0/Android/media/com.example.app/SampleStartupBenchmark_startup-baseline-prof.txt .
    
  9. Renommez le nom du fichier généré en baseline-prof.txt, puis copiez-le dans le répertoire src/main du module de votre application.

Définir manuellement des règles de profil

Vous pouvez définir manuellement des règles de profil dans une application ou un module de bibliothèque en créant un fichier nommé baseline-prof.txt dans le répertoire src/main. Il s'agit du même dossier que celui contenant le fichier AndroidManifest.xml.

Le fichier indique une règle par ligne. Chaque règle représente un modèle de correspondance entre les méthodes ou les classes de l'application ou de la bibliothèque qui doivent être optimisées.

La syntaxe de ces règles correspond à un sur-ensemble du format de profil ART (HRF) intelligible lorsque adb shell profman --dump-classes-and-methods est utilisé. La syntaxe est très semblable à la syntaxe des descripteurs et des signatures, mais elle permet également d'utiliser des caractères génériques pour simplifier le processus d'écriture des règles.

Les exemples suivants présentent quelques règles de profil de référence incluses dans la bibliothèque 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;

Syntaxe de la règle

Ces règles peuvent prendre une ou deux formes pour cibler les méthodes ou les classes :

[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]

Une règle de classe utilise le format suivant :

[CLASS_DESCRIPTOR]
Syntaxe Description
FLAGS Représente un ou plusieurs des caractères H, S et P pour indiquer si cette méthode doit être signalée comme Hot, Startup ou Post Startup par rapport au type de démarrage.

Une méthode avec l'indicateur H indique qu'il s'agit d'une méthode "Hot", ce qui signifie qu'elle est appelée plusieurs fois pendant la durée de vie de l'application.

Une méthode avec l'indicateur S indique qu'il s'agit d'une méthode appelée au démarrage.

Une méthode avec l'option P indique qu'il s'agit d'une méthode "Hot" qui n'est pas liée au démarrage.

Une classe présente dans ce fichier indique qu'elle est utilisée au démarrage et doit être préallouée dans le tas de mémoire pour éviter le coût de chargement de la classe. Le compilateur ART utilise différentes stratégies d'optimisation, telles que la compilation anticipée (ou compilation AOT) de ces méthodes et l'optimisation de la mise en page dans le fichier AOT généré.
CLASS_DESCRIPTOR Descripteur de la classe de méthode ciblée. Par exemple, androidx.compose.runtime.SlotTable aurait un descripteur Landroidx/compose/runtime/SlotTable;. Remarque : Le préfixe L est ajouté ici au format Dalvik Executable (DEX).
METHOD_SIGNATURE Signature de la méthode, y compris son nom, ses types de paramètres et de retour. Par exemple, la méthode

// LayoutNode.kt

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

sur LayoutNode a la signature isPlaced()Z.

Ces modèles peuvent avoir des caractères génériques pour qu'une même règle englobe plusieurs méthodes ou classes. Pour obtenir un guide d'écriture avec la syntaxe des règles dans Android Studio, consultez le plug-in Profils de référence Android.

Exemple de règle avec caractères génériques :

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

Types acceptés dans les règles de profil de référence

Les règles de profil de référence sont compatibles avec les types suivants. Pour en savoir plus sur ces types, consultez le format Dalvik Executable (DEX).

Caractère Type Description
B octet Octet signé
C car. Point de code de caractères Unicode encodés en UTF-16
D double Valeur de double précision à virgule flottante
F float Valeur de simple précision à virgule flottante
I int Nombre entier
J long Entier long
S court Signé court
V void Void
Z booléen Vrai ou faux
L (nom de classe) référence Instance d'un nom de classe

De plus, les bibliothèques peuvent définir des règles qui seront empaquetées dans les artefacts de l'application automatique des recommandations (AAR). Lorsque vous créez un APK pour inclure ces artefacts, les règles sont fusionnées (de la même manière que pour le fichier manifeste) et compilées dans un profil ART binaire compact spécifique à l'APK.

ART exploite ce profil lorsque l'APK est utilisé sur les appareils pour une compilation anticipée d'un sous-ensemble spécifique de l'application au moment de l'installation sur Android 9 (niveau 28 d'API) ou Android 7 (niveau 24 d'API) avec ProfileInstaller.

Remarques supplémentaires

Lorsque vous créez des profils de référence, veuillez prendre en compte les éléments suivants :

  • Android version 5 à 6 (niveaux 21 et 23 d'API) compile déjà de manière anticipée l'APK au moment de l'installation.

  • Les applications pouvant être déboguées ne sont jamais compilées de manière anticipée pour faciliter le dépannage.

  • Les fichiers de règles doivent être nommés baseline-prof.txt et placés dans le répertoire racine de votre ensemble de sources principal (il doit s'agir d'un fichier partageant le même emplacement que votre fichier AndroidManifest.xml).

  • Ces fichiers ne seront utilisés que si vous utilisez le plug-in Android Gradle version 7.1.0-alpha05 ou supérieure (Android Studio Bumblebee Canary 5).

  • Actuellement, Bazel ne permet pas de lire ni de fusionner des profils de référence dans un APK.

  • Les profils de référence ne peuvent dépasser 1,5 Mo compressés. Les bibliothèques et les applications doivent donc s'efforcer de définir un petit ensemble de règles de profil qui maximisent l'effet.

  • Les règles générales qui compilent trop d'applications peuvent ralentir le démarrage en raison de l'augmentation de l'accès au disque. Vous devez tester les performances de vos profils de référence.

Mesurer les améliorations

Automatiser les mesures avec la bibliothèque Macrobenchmark

Les bibliothèques Macrobenchmark vous permettent de contrôler la compilation des mesures préalables via l'API CompilationMode et l'utilisation de BaselineProfile.

Si vous avez déjà configuré un test BaselineProfileRule dans un module Macrobenchmark, vous pouvez en définir un nouveau pour évaluer ses performances :

@RunWith(AndroidJUnit4::class)
class BaselineProfileBenchmark {
  @get:Rule
  val benchmarkRule = MacrobenchmarkRule()

  @Test
  fun startupNoCompilation() {
    startup(CompilationMode.None())
  }

  @Test
  fun startupBaselineProfile() {
    startup(CompilationMode.Partial(
      baselineProfileMode = BaselineProfileMode.Require
    ))
  }

  private fun startup(compilationMode: CompilationMode) {
    benchmarkRule.measureRepeated(
      packageName = "com.example.app",
      metrics = listOf(StartupTimingMetric()),
      iterations = 10,
      startupMode = StartupMode.COLD,
      compilationMode = compilationMode
    ) { // this = MacrobenchmarkScope
        pressHome()
        startActivityAndWait()
    }
  }
}

La figure 2 illustre un exemple de réussite des tests :

Figure 2. Voici les résultats d'un petit test. Les avantages pour les applications volumineuses seront plus importants avec les profils de référence.

Notez que, bien que l'exemple ci-dessus examine StartupTimingMetric, d'autres métriques importantes valent la peine d'être prises en compte, comme À-coups (métriques de frame), qui peut être mesurée à l'aide de Jetpack Macrobenchmark.

Mesurer manuellement les améliorations des applications

Commençons par mesurer, à titre de comparaison, le démarrage d'application non optimisé.

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
# For additional info https://developer.android.com/topic/performance/vitals/launch-time#time-initial
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

À présent, chargeons le profil de référence de manière indépendante.

# Unzip the Release APK first
unzip release.apk
# Create a ZIP archive
# Note: The name should match the name of the APK
# Note: Copy baseline.prof{m} and rename it to 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

Pour vérifier que le package a été optimisé à l'installation, exécutez la commande suivante :

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

Le résultat doit indiquer que le package a été compilé.

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

Nous pouvons maintenant mesurer les performances de démarrage de l'application comme auparavant, mais sans réinitialiser l'état compilé.

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

Problèmes connus

Actuellement, l'utilisation de profils de référence présente plusieurs problèmes connus :

  • Les profils de référence ne sont pas correctement empaquetés lors de la création de l'APK à partir d'un app bundle. Pour résoudre ce problème, appliquez com.android.tools.build:gradle:7.3.0-beta02 ou une version supérieure (problème).

  • Les profils de référence ne sont correctement empaquetés que pour le fichier classes.dex principal. Cela concerne les applications contenant plusieurs fichiers .dex. Pour résoudre ce problème, appliquez com.android.tools.build:gradle:7.3.0-beta02 ou une version supérieure (problème).

  • Macrobenchmark n'est pas compatible avec les profils de référence sur Android 12L (niveau 32 d'API) (problème) et Android 13 (niveau 33 d'API) (problème).

  • La réinitialisation des caches du profil ART n'est pas autorisée sur les builds user (qui ne sont pas en mode root). Pour contourner ce problème, androidx.benchmark:benchmark-macro-junit4:1.1.0-rc02 inclut un correctif qui réinstalle l'application pendant l'analyse comparative (problème).

  • Les profils Android Studio n'installent pas de profils de référence au moment de profiler l'application (problème).

  • Les systèmes de compilation autre que Gradle (Bazel, Buck, etc.) ne permettent actuellement pas de compiler des profils de référence en APK de sortie.

  • Il faut au Play Store entre 10 et 14 heures après l'importation de l'AAB pour rendre les profils de référence disponibles au moment de l'installation. Les utilisateurs qui téléchargent l'application à ce moment ne verront aucun avantage à cette opération tant qu'elle ne sera pas exécutée en arrière-plan (probablement le lendemain). Ceci est en cours d'amélioration.

  • Les canaux de distribution d'application autres que le Play Store peuvent ne pas être compatibles avec les profils de référence lors de l'installation. Les utilisateurs de l'application par ces canaux ne verront les avantages qu'à la fin de la désactivation de l'arrière-plan (probablement le lendemain).