Améliorer les performances de l'application avec les profils de référence

1. Avant de commencer

Cet atelier de programmation vous explique comment générer des profils de référence pour optimiser les performances de votre application et comment vérifier les gains de performances liés à l'utilisation de ces profils.

Ce dont vous aurez besoin

Objectifs de l'atelier

  • Configurer le projet pour utiliser les générateurs de profils de référence.
  • Générer des profils de référence pour optimiser le démarrage de l'application et les performances de défilement.
  • Vérifier les gains de performances avec la bibliothèque Jetpack Macrobenchmark.

Points abordés

  • Définition et utilisation des profils de référence pour améliorer les performances de l'application
  • Génération des profils de référence
  • Gains de performances des profils de référence

2. Configuration

Pour commencer, clonez le dépôt GitHub à partir de la ligne de commande à l'aide de la commande suivante :

$ git clone https://github.com/android/codelab-android-performance.git

Vous pouvez également télécharger deux fichiers ZIP :

Ouvrir le projet dans Android Studio

  1. Dans la fenêtre "Welcome to Android Studio" (Bienvenue dans Android Studio), sélectionnez 61d0a4432ef6d396.png Open an existing Project (Ouvrir un projet existant).
  2. Sélectionnez le dossier [Download Location]/codelab-android-performance/baseline-profiles. Assurez-vous de sélectionner le répertoire baseline-profiles.
  3. Lorsqu'Android Studio importe le projet, assurez-vous de pouvoir exécuter le module app pour créer l'application exemple que vous allez utiliser par la suite.

Application exemple

Dans cet atelier de programmation, vous allez utiliser l'application exemple JetSnack. Il s'agit d'une application de commande de snacks virtuels qui utilise Jetpack Compose.

Pour mesurer les performances de l'application, vous devez comprendre la structure de l'interface utilisateur (UI) et le comportement de l'application afin de pouvoir accéder aux éléments d'UI des analyses comparatives. Exécutez l'application et familiarisez-vous avec les écrans de base en commandant des snacks. Vous n'avez pas besoin de connaître précisément l'architecture de l'application.

23633b02ac7ce1bc.png

3. Définition des profils de référence

Les profils de référence améliorent la vitesse d'exécution du code d'environ 30 % dès le premier lancement en évitant l'interprétation et les étapes de compilation juste à temps (JIT) pour les chemins de code inclus. En expédiant un profil de référence dans une application ou une bibliothèque, Android Runtime (ART) peut optimiser les chemins de code inclus via la compilation anticipée (ou compilation AOT), ce qui permet d'améliorer les performances pour chaque nouvel utilisateur et chaque mise à jour d'application. Cette optimisation guidée des profils (PGO) permet aux applications d'optimiser le démarrage, de réduire les à-coups d'interaction et d'améliorer les performances d'exécution globales pour les utilisateurs finaux dès le premier lancement.

Avec un profil de référence, toutes les interactions utilisateur (telles que le démarrage d'une application, la navigation entre les écrans ou le défilement du contenu) sont plus fluides dès leur première exécution. Augmenter la vitesse et la réactivité d'une application entraîne une augmentation du nombre d'utilisateurs actifs par jour et un taux de visiteurs de retour moyen plus élevé.

Les profils de référence contribuent à guider l'optimisation des performances au-delà du démarrage de l'application en fournissant des interactions utilisateur courantes qui améliorent l'exécution dès le premier lancement. La compilation AOT guidée ne s'appuie pas sur les appareils des utilisateurs et peut être effectuée une fois pour chaque version sur un ordinateur de développement plutôt que sur un appareil mobile. En publiant des versions avec un profil de référence, les optimisations d'application sont disponibles beaucoup plus rapidement qu'en utilisant uniquement les profils cloud.

Lorsque vous n'utilisez pas de profil de référence, le code de l'application fait l'objet d'une compilation JIT dans la mémoire après avoir été interprété, ou dans un fichier odex en arrière-plan lorsque l'appareil est inactif. Les utilisateurs peuvent bénéficier d'une expérience moins optimale la première fois qu'ils exécutent une application après l'avoir installée ou mise à jour, jusqu'à ce que les nouveaux chemins soient optimisés.

4. Configurer le module générateur de profils de référence

Vous pouvez générer des profils de référence avec une classe de test d'instrumentation qui exige d'ajouter un module Gradle au projet. Pour ce faire, le plus simple consiste à utiliser l'assistant du module Android Studio fourni avec Android Studio Hedgehog ou version ultérieure.

Ouvrez la fenêtre de l'assistant de création de module : effectuez un clic droit sur le projet (ou le module) dans le panneau Project (Projet) et cliquez sur New > Module (Nouveau > Module).

232b04efef485e9c.png

Dans la fenêtre qui s'ouvre, sélectionnez Baseline Profile Generator (Générateur de profils de référence) dans le volet "Templates" (Modèles).

b191fe07969e8c26.png

En plus des paramètres habituels (comme le nom du module, le nom du package, la langue ou le langage de la configuration de compilation), deux entrées sortent de l'ordinaire pour la création d'un module : Target application (Application cible) et Use Gradle Managed Device (Utiliser un appareil géré par Gradle).

Target application (Application cible) désigne le module d'application utilisé pour générer les profils de référence correspondants. Si votre projet utilise plusieurs modules d'application, sélectionnez celui pour lequel vous souhaitez exécuter les générateurs.

La case à cocher Use Gradle Managed Device (Utiliser un appareil géré par Gradle) désigne le module à utiliser pour exécuter les générateurs de profils de référence sur les émulateurs Android gérés automatiquement. Pour en savoir plus sur les appareils gérés par Gradle, consultez Adapter vos tests grâce aux appareils gérés par Gradle. Lorsque la case est décochée, les générateurs utilisent n'importent quel appareil connecté.

Lorsque vous avez configuré toutes les informations du nouveau module, cliquez sur Finish (Terminer) pour créer le module.

Modifications apportées par l'assistant de module

L'assistant de module apporte plusieurs modifications à votre projet.

Il ajoute un module Gradle nommé baselineprofile (ou le nom que vous avez sélectionné dans l'assistant).

Ce module utilise le plug-in com.android.test, qui indique à Gradle de ne pas l'inclure dans votre application. Il ne peut donc contenir que du code de test (ou des analyses comparatives). Il applique aussi le plug-in androidx.baselineprofile, qui permet d'automatiser la génération de profils de référence.

L'assistant modifie également le module de l'application cible que vous sélectionnez. Plus spécifiquement, il applique le plug-in androidx.baselineprofile, ajoute la dépendance androidx.profileinstaller et ajoute la dépendance baselineProfile au nouveau module build.gradle(.kts) :

plugins {
  id("androidx.baselineprofile")
}

dependencies {
  // ...
  implementation("androidx.profileinstaller:profileinstaller:1.3.0")
  "baselineProfile"(project(mapOf("path" to ":baselineprofile")))
}

Ajouter la dépendance androidx.profileinstaller permet d'exécuter les actions suivantes :

  • Vérifier en local les gains de performances des profils de référence générés.
  • Utiliser les profils de référence sur Android 7 (niveau d'API 24) et Android 8 (niveau d'API 26), qui ne sont pas compatibles avec les profils cloud.
  • Utiliser les profils de référence sur les appareils qui ne disposent pas des services Google Play.

La dépendance baselineProfile(project(":baselineprofile")) indique à Gradle dans quel module récupérer les profils de référence générés.

Le projet est configuré. Vous allez maintenant écrire une classe de générateur de profils de référence.

5. Écrire un générateur de profils de référence

En général, vous générez des profils de référence pour les parcours utilisateur standard de votre application.

L'assistant de module crée une classe de test BaselineProfileGenerator de base qui permet de générer le profil de référence associé au démarrage de l'application et ressemble à ceci :

@RunWith(AndroidJUnit4::class)
@LargeTest
class BaselineProfileGenerator {

    @get:Rule
    val rule = BaselineProfileRule()

    @Test
    fun generate() {
        rule.collect("com.example.baselineprofiles_codelab") {
            // This block defines the app's critical user journey. This is where you
            // optimize for app startup. You can also navigate and scroll
            // through your most important UI.

            // Start default activity for your app.
            pressHome()
            startActivityAndWait()

            // TODO Write more interactions to optimize advanced journeys of your app.
            // For example:
            // 1. Wait until the content is asynchronously loaded.
            // 2. Scroll the feed content.
            // 3. Navigate to detail screen.

            // Check UiAutomator documentation for more information about how to interact with the app.
            // https://d.android.com/training/testing/other-components/ui-automator
        }
    }
}

Cette classe utilise une règle de test BaselineProfileRule et comporte une méthode de test pour générer le profil. Le point d'entrée pour générer le profil est la fonction collect(). Elle ne nécessite que deux paramètres :

  • packageName : le package de l'application
  • profileBlock : le dernier paramètre lambda

Dans le paramètre lambda profileBlock, spécifiez les interactions qui couvrent les parcours utilisateur standard de l'application. La bibliothèque exécute profileBlock plusieurs fois et collecte les classes et les fonctions appelées, et génère le profil de référence sur l'appareil avec le code à optimiser.

Par défaut, la classe de générateur créée comporte les interactions pour démarrer l'Activity par défaut. Elle attend que le premier frame de l'application s'affiche avec la méthodestartActivityAndWait().

Étendre le générateur avec les parcours client

Notez que la classe générée comporte aussi plusieurs TODO pour écrire d'autres interactions et optimiser les parcours client de votre application. Cette action est recommandée pour optimiser les performances après le démarrage de l'application.

Dans l'application exemple, vous pouvez identifier ces parcours comme suit :

  1. Démarrez l'application. Cette étape est déjà en partie réalisée par la classe générée.
  2. Attendez que le chargement asynchrone du contenu se termine.
  3. Faites défiler la liste de snacks.
  4. Accédez aux informations détaillées des snacks.

Modifier le générateur pour qu'il intègre les fonctions indiquées qui couvrent les parcours standard dans l'extrait suivant :

// ...
rule.collect("com.example.baselineprofiles_codelab") {
    // This block defines the app's critical user journey. This is where you
    // optimize for app startup. You can also navigate and scroll
    // through your most important UI.

    // Start default activity for your app.
    pressHome()
    startActivityAndWait()

    // TODO Write more interactions to optimize advanced journeys of your app.
    // For example:
    // 1. Wait until the content is asynchronously loaded.
    waitForAsyncContent()
    // 2. Scroll the feed content.
    scrollSnackListJourney()
    // 3. Navigate to detail screen.
    goToSnackDetailJourney()

    // Check UiAutomator documentation for more information about how to interact with the app.
    // https://d.android.com/training/testing/other-components/ui-automator
}
// ...

Écrivez maintenant les interactions pour chaque parcours mentionné. Vous pouvez les écrire en tant que fonction d'extension de MacrobenchmarkScope, afin d'avoir accès aux paramètres et aux fonctions qu'il fournit. Vous pouvez ainsi réutiliser les interactions avec les analyses comparatives pour vérifier les gains de performances.

Attendre le chargement asynchrone du contenu

Au démarrage, de nombreuses applications font l'objet d'un chargement asynchrone (aussi connu sous le nom d'état d'affichage intégral), qui indique au système que le contenu est chargé et rendu, et que l'utilisateur peut interagir avec. Attendez l'état dans le générateur (waitForAsyncContent) et effectuez les interactions suivantes :

  1. Recherchez la liste de snacks.
  2. Attendez que certains éléments de la liste soient visibles à l'écran.
fun MacrobenchmarkScope.waitForAsyncContent() {
   device.wait(Until.hasObject(By.res("snack_list")), 5_000)
   val contentList = device.findObject(By.res("snack_list"))
   // Wait until a snack collection item within the list is rendered.
   contentList.wait(Until.hasObject(By.res("snack_collection")), 5_000)
}

Parcours de défilement d'une liste

Pour le parcours de défilement d'une liste de snacks (scrollSnackListJourney), vous pouvez suivre ces interactions :

  1. Recherchez l'élément d'UI correspondant à la liste de snacks.
  2. Définissez les marges de manœuvre pour qu'elles ne déclenchent pas la navigation système.
  3. Faites défiler la liste et attendez que l'UI s'arrête.
fun MacrobenchmarkScope.scrollSnackListJourney() {
   val snackList = device.findObject(By.res("snack_list"))
   // Set gesture margin to avoid triggering gesture navigation.
   snackList.setGestureMargin(device.displayWidth / 5)
   snackList.fling(Direction.DOWN)
   device.waitForIdle()
}

Parcours d'accès aux informations détaillées

Le dernier parcours (goToSnackDetailJourney) implémente les interactions suivantes :

  1. Recherchez la liste de snacks et tous les snacks que vous pouvez utiliser.
  2. Sélectionnez un élément dans la liste.
  3. Cliquez sur l'élément et attendez que l'écran des informations détaillées soit chargé. Vous pouvez partir du fait que la liste de snacks ne s'affiche plus à l'écran.
fun MacrobenchmarkScope.goToSnackDetailJourney() {
    val snackList = device.findObject(By.res("snack_list"))
    val snacks = snackList.findObjects(By.res("snack_item"))
    // Select snack from the list based on running iteration.
    val index = (iteration ?: 0) % snacks.size
    snacks[index].click()
    // Wait until the screen is gone = the detail is shown.
    device.wait(Until.gone(By.res("snack_list")), 5_000)
}

Vous avez défini toutes les interactions nécessaires pour que le générateur de profils de référence soit prêt. Vous devez maintenant configurer l'appareil sur lequel l'exécuter.

6. Préparer un appareil sur lequel exécuter le générateur

Pour générer des profils de référence, nous vous recommandons d'utiliser un émulateur comme un appareil géré par Gradle, ou un appareil exécutant Android 13 (API 33) ou version ultérieure.

Vous pouvez utiliser un appareil géré par Gradle pour profiter d'un processus reproductible et automatiser la génération des profils de référence. Utiliser un appareil géré par Gradle permet d'exécuter des tests sur un émulateur Android sans avoir à le lancer ni à le supprimer manuellement. Pour en savoir plus sur les appareils gérés par Gradle, consultez Adapter vos tests grâce aux appareils gérés par Gradle.

Pour définir un appareil géré par Gradle, ajouter sa définition au fichier build.gradle.kts du module :baselineprofile, comme représenté dans l'extrait suivant :

android {
  // ...

  testOptions.managedDevices.devices {
    create<ManagedVirtualDevice>("pixel6Api31") {
        device = "Pixel 6"
        apiLevel = 31
        systemImageSource = "aosp"
    }
  }
}

Dans ce cas, nous utilisons Android 11 (niveau d'API 31). L'image système aosp permet un accès en mode root.

Configurez ensuite le plug-in Baseline Profile Gradle pour utiliser l'appareil géré par Gradle défini. Pour ce faire, ajoutez le nom de l'appareil dans la propriété managedDevices et désactivez useConnectedDevices comme représenté dans l'extrait suivant :

android {
  // ...
}

baselineProfile {
   managedDevices += "pixel6Api31"
   useConnectedDevices = false
}

dependencies {
  // ...
}

Générez maintenant un profil de référence.

7. Générer le profil de référence

Lorsque l'appareil est prêt, vous pouvez créer un profil de référence. Le plug-in Baseline Profile Gradle crée les tâches Gradle pour automatiser l'intégralité du processus d'exécution de la classe de test de générateur et appliquer les profils de référence générés à l'application.

L'assistant de création de module a créé une configuration d'exécution capable de réaliser rapidement la tâche Gradle avec tous les paramètres nécessaires sans devoir basculer entre le terminal et Android Studio.

Pour l'exécuter, localisez la configuration d'exécution Generate Baseline Profile et cliquez sur le bouton Run (Exécuter) 599be5a3531f863b.png.

6911ecf1307a213f.png

La tâche lance l'image de l'émulateur défini plus tôt. Exécutez les interactions de la classe de test BaselineProfileGenerator plusieurs fois. Après quoi, supprimez l'émulateur et envoyez les résultats à Android Studio.

Lorsque le générateur a abouti, le plug-in Gradle place automatiquement le baseline-prof.txt généré dans l'application cible (module :app) du dossier src/release/generated/baselineProfile/.

fa0f52de5d2ce5e8.png

Exécuter le générateur à partir de la ligne de commande (facultatif)

Vous pouvez également exécuter le générateur à partir de la ligne de commande. Vous pouvez exploiter la tâche créée par l'appareil géré par Gradle (:app:generateBaselineProfile). Cette commande exécute tous les tests du projet défini par la dépendance baselineProfile(project(:baselineProfile)). Le module comporte également des analyses comparatives visant à vérifier ultérieurement les gains de performances. De ce fait, les tests échouent et affichent un avertissement concernant l'exécution d'analyses comparatives sur un émulateur.

android
   .testInstrumentationRunnerArguments
   .androidx.benchmark.enabledRules=BaselineProfile

Pour cela, vous pouvez filtrer les générateurs de profils de référence avec l'argument d'exécuteur d'instrumentation suivant, qui permet d'ignorer toutes les analyses comparatives.

La commande complète se présente comme suit :

./gradlew :app:generateBaselineProfile -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile

Distribuer l'application avec les profils de référence

Une fois que les profils de référence ont été générés et copiés dans le code source de l'application, compilez la version de production de l'application comme d'habitude. Vous n'avez rien à faire de plus pour distribuer les profils de référence aux utilisateurs. Ils sont sélectionnés par le plug-in Android Gradle pendant la compilation, puis intégrés au fichier AAB ou APK. Vous devez ensuite importer la compilation dans Google Play.

Lorsque les utilisateurs installent l'application (ou la mettent à jour depuis la version précédente), le profil de référence est également installé. Cela améliore les performances lors de la première exécution de l'application.

Vous allez maintenant vérifier dans quelle mesure les profils de référence améliorent les performances de l'application.

8. Personnaliser la génération de profil de référence (facultatif)

Le plug-in Baseline Profile Gradle comporte plusieurs options pour personnaliser la génération des profils selon vos besoins. Vous pouvez modifier le comportement avec le bloc de configuration baselineProfile { } des scripts de compilation.

Le bloc de configuration du module :baselineprofile modifie l'exécution des générateurs avec la possibilité d'ajouter des managedDevices et de déterminer s'il faut employer useConnectedDevices ou un appareil géré par Gradle.

Le bloc de configuration du module cible :app détermine si les profils sont enregistrés et la façon dont ils sont générés. Vous pouvez modifier les paramètres suivants :

  • automaticGenerationDuringBuild : lorsqu'il est activé, vous pouvez générer le profil de référence lors de la compilation du build de production. Il est particulièrement utile lorsque la compilation utilise l'intégration continue (CI) avant de publier l'application.
  • saveInSrc : détermine si les profils de référence générés sont stockés dans le dossier src/. Vous pouvez également accéder au fichier depuis le dossier de compilation :baselineprofile.
  • baselineProfileOutputDir : définit où stocker les profils de référence générés.
  • mergeIntoMain : par défaut, les profils de référence sont générés par variante de compilation (type de produit et type de compilation). Si vous souhaitez fusionner tous les profils dans src/main, activez cet indicateur.
  • filter : vous pouvez filtrer les classes ou les méthodes à inclure ou exclure des profils de référence générés. Cela est particulièrement utile aux développeurs de bibliothèques qui souhaitent utiliser uniquement le code de la bibliothèque incluse.

9. Vérifier les gains de performances au démarrage

Une fois que le profil de référence a été généré et ajouté à l'application, vérifiez que son effet sur les performances de l'application est celui escompté.

L'assistant de création de module crée une classe d'analyse comparative appelée StartupBenchmarks. Elle comporte une analyse comparative visant à mesurer le temps de démarrage de l'application et à le comparer à celui obtenu lorsque l'application utilise les profils de référence.

La classe ressemble à ceci :

@RunWith(AndroidJUnit4::class)
@LargeTest
class StartupBenchmarks {

    @get:Rule
    val rule = MacrobenchmarkRule()

    @Test
    fun startupCompilationNone() =
        benchmark(CompilationMode.None())

    @Test
    fun startupCompilationBaselineProfiles() =
        benchmark(CompilationMode.Partial(BaselineProfileMode.Require))

    private fun benchmark(compilationMode: CompilationMode) {
        rule.measureRepeated(
            packageName = "com.example.baselineprofiles_codelab",
            metrics = listOf(StartupTimingMetric()),
            compilationMode = compilationMode,
            startupMode = StartupMode.COLD,
            iterations = 10,
            setupBlock = {
                pressHome()
            },
            measureBlock = {
                startActivityAndWait()

                // TODO Add interactions to wait for when your app is fully drawn.
                // The app is fully drawn when Activity.reportFullyDrawn is called.
                // For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
                // from the AndroidX Activity library.

                // Check the UiAutomator documentation for more information on how to
                // interact with the app.
                // https://d.android.com/training/testing/other-components/ui-automator
            }
        )
    }
}

Elle utilise MacrobenchmarkRule qui permet d'exécuter des analyses comparatives de l'application et de recueillir des indicateurs de performance. Le point d'entrée pour écrire une analyse comparative est la fonction measureRepeated de la règle.

Elle nécessite plusieurs paramètres :

  • packageName: : identifie l'application à évaluer.
  • metrics : détermine le type d'informations que vous souhaitez mesurer pendant l'analyse comparative.
  • iterations : détermine le nombre de répétitions de l'analyse comparative.
  • startupMode : définit comment l'application doit démarrer au lancement de l'analyse comparative.
  • setupBlock : identifie les interactions avec l'application qui doivent se produire avant la mesure.
  • measureBlock : définit les interactions avec l'application que vous souhaitez évaluer pendant l'analyse comparative.

La classe de test comporte aussi deux tests : startupCompilationeNone() et startupCompilationBaselineProfiles(), qui appelle la fonction benchmark() avec un autre compilationMode.

CompilationMode

Le paramètre CompilationMode définit la manière dont l'application est précompilée dans le code machine. Il propose les options suivantes :

  • DEFAULT : l'application est précompilée partiellement à l'aide de profils de référence, le cas échéant. Cette option est utilisée si aucun paramètre compilationMode n'est appliqué.
  • None() : l'état de la compilation est réinitialisé, et l'application n'est pas précompilée. La compilation juste à temps (JIT) reste activée lors de l'exécution de l'application.
  • Partial() : l'application est précompilée à l'aide de profils de référence et/ou d'exécutions d'échauffement.
  • Full() : l'ensemble du code d'application est précompilé. Il s'agit de la seule option disponible sur Android 6 (API 23) ou version antérieure.

Si vous souhaitez commencer à optimiser les performances de l'application, vous pouvez choisir le mode de compilation DEFAULT, car les performances sont similaires à celles obtenues lorsque l'application est installée depuis Google Play. Si vous souhaitez comparer les gains de performances offerts par les profils de référence, vous pouvez évaluer les résultats du mode de compilation None et Partial.

Modifier l'analyse comparative pour attendre le chargement du contenu

L'écriture des analyses comparatives est semblable à celle des générateurs de profils de référence et consiste à écrire les interactions avec l'application. Par défaut, les analyses comparatives créées attendent uniquement le rendu du premier frame (comme pour le BaselineProfileGenerator). Nous recommandons donc de l'améliorer de sorte à attendre le chargement asynchrone du contenu.

Pour ce faire, vous pouvez réutiliser les fonctions d'extension que vous avez écrites pour le générateur. Cette analyse comparative capture les temps de démarrage (en utilisant StartupTimingMetric()). Nous recommandons donc d'inclure uniquement l'attente liée au chargement asynchrone du contenu, puis d'écrire une analyse comparative distincte pour les autres parcours utilisateur définis dans le générateur.

// ...
measureBlock = {
   startActivityAndWait()

   // The app is fully drawn when Activity.reportFullyDrawn is called.
   // For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
   // from the AndroidX Activity library.
   waitForAsyncContent() // <------- Added to wait for async content.

   // Check the UiAutomator documentation for more information on how to
   // interact with the app.
   // https://d.android.com/training/testing/other-components/ui-automator
}

Exécuter les analyses comparatives

Vous pouvez exécuter les analyses comparatives de la même manière que les tests d'instrumentation. Vous pouvez exécuter la fonction de test ou l'intégralité de la classe avec l'icône en forme de gouttière qui se trouve à côté.

587b04d1a76d1e9d.png

Assurez-vous d'avoir sélectionné un appareil physique. Dans le cas contraire, les analyses comparatives sur l'émulateur Android échouent lors de l'exécution et un avertissement indique que les résultats peuvent être incorrects. Bien que vous puissiez techniquement l'exécuter sur un émulateur, vous mesurez les performances de la machine hôte. Si la charge est importante, les analyses comparatives sont plus lentes, et inversement.

94e0da86b6f399d5.png

Une fois que l'analyse comparative exécutée, l'application est recompilée, puis exécute vos analyses. Les analyses comparatives démarrent, s'arrêtent et réinstallent même votre application plusieurs fois en fonction des iterations que vous avez définies.

Une fois les analyses comparatives terminées, les temps s'affichent dans la sortie Android Studio, comme représenté dans la capture d'écran suivante :

282f90d5f6ff5196.png

Sur la capture d'écran, notez que le temps de démarrage de l'application est différent pour chaque CompilationMode. Les valeurs médianes sont indiquées dans le tableau suivant :

timeToInitialDisplay [ms]

timeToFullDisplay [ms]

Aucune

202,2

818,8

Profils de référence

193,7

637,9

Amélioration

4 %

28 %

La différence entre les modes de compilation pour timeToFullDisplay est de 180 ms, soit une amélioration d'env. 28 % en appliquant simplement un profil de référence. Le mode CompilationNone fonctionne moins bien, car l'appareil doit effectuer la majorité de la compilation JIT au démarrage de l'application. Le mode CompilationBaselineProfiles fonctionne mieux, car la compilation AOT partielle avec profils de référence compile le code que l'utilisateur est le plus susceptible d'utiliser et ne précompile pas le code non critique, de sorte qu'il n'est pas nécessaire de le charger dans l'immédiat.

10. Vérifier l'amélioration des performances de défilement (facultatif)

Comme à l'étape précédente, vous pouvez mesurer et vérifier les performances de défilement. Commencez par créer la classe de test ScrollBenchmarks avec la règle d'analyse comparative et deux méthodes de test qui utilisent des modes de compilation différents :

@LargeTest
@RunWith(AndroidJUnit4::class)
class ScrollBenchmarks {

   @get:Rule
   val rule = MacrobenchmarkRule()

   @Test
   fun scrollCompilationNone() = scroll(CompilationMode.None())

   @Test
   fun scrollCompilationBaselineProfiles() = scroll(CompilationMode.Partial())

   private fun scroll(compilationMode: CompilationMode) {
       // TODO implement
   }
}

Depuis la méthode scroll, utilisez la fonction measureRepeated avec les paramètres requis. Pour le paramètre metrics, utilisez FrameTimingMetric, qui mesure le temps nécessaire pour produire les frames de l'UI :

private fun scroll(compilationMode: CompilationMode) {
   rule.measureRepeated(
       packageName = "com.example.baselineprofiles_codelab",
       metrics = listOf(FrameTimingMetric()),
       compilationMode = compilationMode,
       startupMode = StartupMode.WARM,
       iterations = 10,
       setupBlock = {
           // TODO implement
       },
       measureBlock = {
           // TODO implement
       }
   )
}

Cette fois, vous devez davantage répartir les interactions entre le setupBlock et le measureBlock pour mesurer uniquement la durée des frames entre la première mise en page et le défilement du contenu. Par conséquent, placez les fonctions qui démarrent l'écran par défaut sur le setupBlock, et les fonctions d'extension déjà créées waitForAsyncContent() et scrollSnackListJourney() sur le measureBlock :

private fun scroll(compilationMode: CompilationMode) {
   rule.measureRepeated(
       packageName = "com.example.baselineprofiles_codelab",
       metrics = listOf(FrameTimingMetric()),
       compilationMode = compilationMode,
       startupMode = StartupMode.WARM,
       iterations = 10,
       setupBlock = {
           pressHome()
           startActivityAndWait()
       },
       measureBlock = {
           waitForAsyncContent()
           scrollSnackListJourney()
       }
   )
}

Lorsque l'analyse comparative est prête, exécutez-la comme auparavant pour obtenir des résultats comme ceux représentés dans la capture d'écran suivante :

84aa99247226fc3a.png

FrameTimingMetric renvoie la durée des frames en millisecondes (frameDurationCpuMs) aux 50e, 90e, 95e et 99e centiles. Sur Android 12 (niveau d'API 31) ou version ultérieure, cette valeur indique également la durée pendant laquelle les frames ont dépassé la limite (frameOverrunMs). Cette valeur peut être négative, indiquant qu'il restait du temps pour générer le frame.

Les résultats révèlent que les CompilationBaselineProfiles présentent en moyenne une durée de frame plus courte de 2 ms, ce que les utilisateurs ne perçoivent pas nécessairement. En revanche, pour les autres centiles, les résultats sont plus évidents. Pour P99, la différence est de 43,5 ms, soit plus de trois frames ignorés sur un appareil avec un FPS de 90. Par exemple, pour le Pixel 6, 1 000 ms / 90 FPS = environ 11 ms maximum pour l'affichage d'un frame.

11. Félicitations

Félicitations, vous avez terminé cet atelier de programmation et amélioré les performances de votre application grâce aux profils de référence.

Ressources supplémentaires

Consultez les ressources supplémentaires suivantes :

Documents de référence