Présentation de la mesure des performances des applications

Ce document vous aide à identifier et à résoudre les principaux problèmes de performances dans votre application.

Principaux problèmes de performances

De nombreux problèmes peuvent nuire aux performances d'une application, mais voici les plus courants :

Latence de démarrage

La latence de démarrage désigne le temps écoulé entre l'appui sur l'icône d'une application, une notification ou un autre point d'entrée, et l'affichage des données de l'utilisateur à l'écran.

Visez les objectifs de démarrage suivants dans vos applications :

  • Démarrage à froid en moins de 500 ms. Un démarrage à froid se produit lorsque l'application en cours de lancement n'est pas présente dans la mémoire du système. Cela se produit lorsqu'il s'agit du premier lancement de l'application depuis le redémarrage ou lorsque le processus de l'application est arrêté par l'utilisateur ou le système.

    En revanche, un démarrage tiède se produit lorsque l'application s'exécute déjà en arrière-plan. Un démarrage à froid nécessite le plus de travail du système, car il doit tout charger à partir du stockage et initialiser l'application. Essayez d'effectuer des démarrages à froid en 500 ms au maximum.

  • Des latences P95 et P99 très proches de la latence médiane. Lorsque l'application met beaucoup de temps à démarrer, l'expérience utilisateur est médiocre. Les communications inter-processus (IPC) et les E/S inutiles lors du processus critique de démarrage d'une application peuvent subir des conflits de verrouillage et introduire des incohérences.

Défilement par à-coups

À-coups est le terme qui décrit le problème visuel qui se produit lorsque le système n'est pas en mesure de construire et de fournir des frames à temps pour qu'ils soient affichés à l'écran à la cadence demandée de 60 Hz ou plus. Les à-coups sont particulièrement visibles en cas de défilement, alors que le flux d'animation doit être fluide. Des à-coups apparaissent lorsque le mouvement s'interrompt pendant la lecture d'un ou de plusieurs frames, car l'affichage du contenu de l'application prend plus de temps que la durée d'un frame sur le système.

Les applications doivent cibler des fréquences d'actualisation de 90 Hz. Les fréquences de rendu traditionnelles sont de 60 Hz, mais de nombreux appareils plus récents fonctionnent en mode 90 Hz lors d'interactions utilisateur telles que le défilement. Certains appareils acceptent des fréquences encore plus élevées, jusqu'à 120 Hz.

Pour connaître la fréquence d'actualisation utilisée par un appareil à un moment donné, activez une superposition en sélectionnant Options pour les développeurs > Voir la fréquence d'actualisation dans la section Débogage.

Transitions manquant de fluidité

Ces problèmes surviennent lors des interactions, par exemple lorsque vous passez d'un onglet à l'autre ou chargez une nouvelle activité. Ces types de transitions doivent être des animations fluides et ne pas inclure de retards ni de scintillements visuels.

Inefficacité énergétique

Toute tâche consomme de la batterie et les tâches inutiles réduisent l'autonomie de la batterie.

Les allocations de mémoire, qui proviennent de la création d'objets dans le code, peuvent entraîner un travail important dans le système. En effet, non seulement les allocations elles-mêmes nécessitent un effort de la part de l'environnement d'exécution Android Runtime (ART), mais la libération de ces objets ultérieurement (récupération de mémoire) nécessite également du temps et des efforts. L'allocation et la collecte sont bien plus rapides et efficaces, en particulier pour les objets temporaires. Auparavant, il était recommandé d'éviter d'allouer des objets dans la mesure du possible. Toutefois, nous vous recommandons de faire ce qui convient le mieux à votre application et à votre architecture. Économiser sur les allocations au risque que le code devienne ingérable n'est pas le choix idéal, étant donné les capacités de l'ART.

Cependant, cela demande un certain effort. Gardez donc en tête que l'allocation de nombreux objets dans votre boucle interne pourrait entraîner des problèmes de performances.

Identifier les problèmes

Nous vous recommandons de suivre le workflow ci-dessous pour identifier et résoudre les problèmes de performances :

  1. Identifiez et inspectez les critical user journeys suivants :
    • Les flux de démarrage courants, y compris depuis le lanceur d'applications et les notifications.
    • Les écrans sur lesquels l'utilisateur fait défiler les données.
    • Les transitions entre les écrans.
    • Les flux de longue durée, comme la navigation ou la lecture de musique.
  2. Inspectez ce qui se passe au cours des flux précédents à l'aide des outils de débogage suivants :
    • Perfetto: permet de voir ce qui se passe sur l'ensemble de l'appareil, à l'aide de données temporelles précises.
    • Profileur de mémoire: permet de voir quelles allocations de mémoire se produisent sur le tas de mémoire.
    • Simpleperf: affiche un FlameGraph qui identifie les appels de fonction qui utilisent le plus le processeur au cours d'une période donnée. Si vous identifiez un élément qui prend beaucoup de temps dans Systrace, mais que vous ne savez pas pourquoi, Simpleperf peut fournir des informations supplémentaires.

Pour comprendre et déboguer ces problèmes de performances, il est essentiel de déboguer manuellement chaque exécution de test. Vous ne pouvez pas remplacer les étapes précédentes par une analyse de données agrégées. Toutefois, pour comprendre ce que les utilisateurs voient réellement et identifier quand des régressions peuvent se produire, il est important de configurer la collecte de métriques dans des tests automatisés et dans le champ :

  • Flux de démarrage
  • À-coups
    • Métriques de champ
      • Données essentielles liées aux frames de la Play Console : il n'est pas possible de limiter les métriques à un parcours utilisateur spécifique depuis la Play Console. Seuls les à-coups globaux dans l'application sont signalés.
      • Mesure personnalisée avec FrameMetricsAggregator: vous pouvez utiliser FrameMetricsAggregator pour enregistrer les métriques d'à-coups pendant un workflow particulier.
    • Tests labo
      • Défilement avec Macrobenchmark.
      • Macrobenchmark collecte le temps de rendu à l'aide de commandes dumpsys gfxinfo qui regroupent un seul parcours utilisateur. Il s'agit d'un moyen de comprendre les variations d'à-coups sur un parcours utilisateur spécifique. Les métriques RenderTime, qui mettent en évidence le temps nécessaire pour dessiner les frames, sont plus importantes que le nombre de frames saccadés pour identifier les régressions ou les améliorations.

Les liens vers une application sont des liens profonds basés sur l'URL de votre site Web et qui sont validés pour appartenir à votre site Web. Les raisons suivantes peuvent entraîner l'échec de la validation des liens vers une application.

  • Champs d'application des filtres d'intent: n'ajoutez autoVerify aux filtres d'intent que pour les URL auxquelles votre application peut répondre.
  • Changements de protocole non validés: les redirections non validées côté serveur et de sous-domaine sont considérées comme des risques de sécurité et échouent à la validation. Ils entraînent l'échec de tous les liens autoVerify. Par exemple, la redirection des liens de HTTP vers HTTPS, tels que example.com vers www.example.com, sans valider les liens HTTPS, peut entraîner un échec de validation. Veillez à vérifier les liens vers une application en ajoutant des filtres d'intent.
  • Liens non vérifiables: l'ajout de liens non vérifiables à des fins de test peut empêcher le système de valider les liens vers votre application.
  • Serveurs non fiables: assurez-vous que vos serveurs peuvent se connecter à vos applications clientes.

Configurer votre application pour l'analyse des performances

Une configuration appropriée est essentielle pour obtenir des analyses comparatives précises, reproductibles et exploitables à partir d'une application. Effectuez des tests sur un système le plus proche possible de la production, tout en supprimant les sources de bruit. Les sections suivantes présentent plusieurs étapes spécifiques à l'APK et au système que vous pouvez suivre pour préparer une configuration de test. Certaines d'entre elles sont spécifiques à un cas d'utilisation.

Points de trace

Les applications peuvent instrumenter leur code avec des événements de trace personnalisés.

Lors de la capture des traces, le traçage implique une légère surcharge d'environ 5 μs par section. Par conséquent, ne l'utilisez pas pour toutes les méthodes. Le traçage de segments de tâche plus larges (> 0,1 ms) peut fournir des informations importantes sur les goulots d'étranglement.

Remarques sur l'APK

Les variantes de débogage peuvent être utiles pour résoudre les problèmes et symboliser les échantillons de pile, mais elles ont de graves répercussions sur les performances. Les appareils équipés d'Android 10 (niveau d'API 29) ou version ultérieure peuvent utiliser profileable android:shell="true" dans leur fichier manifeste pour activer le profilage dans les builds.

Utilisez votre configuration de minification de code de niveau production. Selon les ressources utilisées par votre application, cela peut avoir un impact important sur les performances. Certaines configurations ProGuard suppriment les points de trace. Par conséquent, pensez à supprimer ces règles pour la configuration sur laquelle vous exécutez des tests.

Compilation

Compilez votre application sur l'appareil dans un état connu, généralement speed ou speed-profile. L'activité juste-à-temps (JIT) en arrière-plan peut avoir un impact important sur les performances, et vous l'atteignez souvent si vous réinstallez l'APK entre deux exécutions de test. Pour ce faire, exécutez la commande suivante :

adb shell cmd package compile -m speed -f com.google.packagename

Le mode de compilation speed compile complètement l'application. Le mode speed-profile compile l'application en fonction d'un profil des chemins de code utilisés qui sont collectés lors de l'utilisation de l'application. Il peut être difficile de collecter des profils de manière cohérente et correcte. Par conséquent, si vous décidez de les utiliser, vérifiez qu'ils collectent les éléments attendus. Les profils se trouvent à l'emplacement suivant :

/data/misc/profiles/ref/[package-name]/primary.prof

Macrobenchmark vous permet de spécifier directement un mode de compilation.

Remarques concernant le système

Pour les mesures de bas niveau et de haute fidélité, calibrez vos appareils. Effectuez des comparaisons A/B sur le même appareil et la même version d'OS. Il peut exister des variations importantes des performances, même sur le même type d'appareil.

Sur les appareils en mode root, envisagez d'utiliser un script lockClocks pour les microbenchmarks. Ces scripts effectuent les opérations suivantes :

  • Réglage des processeurs à une fréquence fixe
  • Désactivation des petits cœurs et configuration du GPU
  • Désactivation de la limitation thermique

Nous vous déconseillons d'utiliser un script lockClocks pour les tests axés sur l'expérience utilisateur, tels que le lancement d'une application, les tests DoU et les tests d'à-coups. Toutefois, cela peut s'avérer essentiel pour réduire le bruit dans les tests Microbenchmark.

Dans la mesure du possible, envisagez d'utiliser un framework de test tel que Macrobenchmark, qui peut réduire le bruit et éviter les erreurs de mesure.

Démarrage lent de l'application : activité de trampoline inutile

Une activité de trampoline peut prolonger inutilement le démarrage d'une application. Il est important de savoir si votre application est concernée. Comme le montre l'exemple de trace suivant, un événement activityStart est immédiatement suivi d'un autre événement activityStart sans qu'aucun frame ne soit dessiné par la première activité.

alt_text Figure 1. Trace montrant une activité de trampoline

Cela peut se produire à la fois dans un point d'entrée de notification et dans un point d'entrée de démarrage d'application standard, et le problème peut souvent être résolu par la refactorisation. Par exemple, si vous utilisez cette activité pour effectuer la configuration avant l'exécution d'une autre activité, intégrez ce code dans un composant ou une bibliothèque réutilisables.

Les allocations inutiles déclenchent des récupérations de mémoire fréquentes

Vous remarquerez peut-être que les récupérations de mémoire se produisent plus fréquemment que prévu dans une trace Systrace.

Dans l'exemple suivant, toutes les 10 secondes au cours d'une opération de longue durée, cela indique que votre application alloue inutilement et de manière cohérente au fil du temps :

alt_text Figure 2 : Trace montrant l'espace entre les événements de récupération de mémoire

Vous remarquerez peut-être également qu'une pile d'appel spécifique effectue la grande majorité des allocations lorsque vous utilisez le Profileur de mémoire. Vous n'avez pas besoin d'éliminer toutes les allocations de manière agressive, car cela peut rendre le code plus difficile à gérer. Commencez plutôt par travailler sur les hotspots des allocations.

Frames saccadés

Le pipeline graphique est relativement complexe, et il peut être difficile de déterminer si un utilisateur a finalement subi une perte de fréquence de frames. Dans certains cas, la plate-forme peut "sauver" un frame grâce à la mise en mémoire tampon. Cependant, vous pouvez ignorer la plupart de ces nuances pour identifier les frames problématiques du point de vue de votre application.

Lorsque les frames sont dessinés avec un effort minimal de l'application, les points de trace Choreographer.doFrame() se produisent à une cadence de 16,7 ms sur un appareil de 60 FPS :

alt_text Figure 3 : Trace affichant des frames rapides fréquents

Si vous faites un zoom arrière et que vous parcourez la trace, vous verrez parfois que les frames prennent un peu plus de temps, mais cela ne pose pas de problème, car ils ne prennent pas plus de 16,7 ms.

alt_text Figure 4. Trace affichant des frames rapides fréquents associés à des pics de travail périodiques

Lorsque vous constatez une interruption de cette cadence régulière, il s'agit d'un frame saccadé, comme illustré dans la figure 5 :

alt_text Figure 5. Trace montrant un frame saccadé

Vous pouvez vous entraîner à les identifier.

alt_text Figure 6. Trace affichant plus de frames saccadés

Dans certains cas, vous devrez effectuer un zoom avant sur un point de trace pour en savoir plus sur les vues gonflées ou sur ce que fait RecyclerView. Dans d'autres cas, vous devrez peut-être procéder à une inspection plus approfondie.

Pour en savoir plus sur l'identification des frames saccadés et le débogage de leurs causes, consultez Affichage lent.

Erreurs RecyclerView courantes

L'invalidation inutile de l'intégralité des données de sauvegarde de RecyclerView peut entraîner de longs délais d'affichage des frames et des à-coups. Pour réduire le nombre de vues à mettre à jour, n'invalidez que les données modifiées.

Consultez Présenter des données dynamiques pour éviter les appels notifyDatasetChanged() coûteux qui entraînent la mise à jour du contenu au lieu de le remplacer complètement.

Si vous ne prenez pas correctement en charge chaque RecyclerView imbriqué, le RecyclerView interne peut être entièrement recréé à chaque fois. Chaque RecyclerView imbriqué interne doit disposer d'un RecycledViewPool défini pour garantir que les vues peuvent être recyclées entre chaque RecyclerView interne.

Si la récupération préalable de données est insuffisante ou non opportune, il peut être pénible d'atteindre le bas d'une liste déroulante lorsqu'un utilisateur doit attendre plus de données du serveur. Bien que cela ne soit pas techniquement un "à-coup", car aucune date limite d'affichage n'est ratée, vous pouvez améliorer considérablement l'expérience utilisateur pour modifier le délai et la quantité de préchargement afin que l'utilisateur ne doive pas attendre les données.

Déboguer votre application

Voici différentes méthodes de débogage des performances de votre application. La vidéo suivante présente le traçage système et l'utilisation du profileur Android Studio.

Déboguer le démarrage de l'application avec Systrace

Consultez la section Temps de démarrage de l'application pour obtenir un aperçu du processus de démarrage de l'application, et regardez la vidéo suivante pour un aperçu du traçage système.

Vous pouvez distinguer les types de démarrage aux étapes suivantes:

  • Démarrage à froid: commencez par créer un processus sans état enregistré.
  • Démarrage à chaud: recrée l'activité tout en réutilisant le processus, ou recrée le processus avec l'état enregistré.
  • Démarrage à chaud: redémarre l'activité et commence lors du gonflement.

Nous vous recommandons de capturer les traces Systraces avec l'application de traçage système sur l'appareil. Pour Android 10 ou version ultérieure, utilisez Perfetto. Pour Android 9 ou une version antérieure, utilisez Systrace. Nous vous recommandons également de consulter les fichiers de suivi à l'aide du lecteur de traces Perfetto basé sur le Web. Pour en savoir plus, consultez la page Présentation du traçage système.

Voici quelques éléments à rechercher:

  • Surveillez les conflits: la concurrence pour les ressources protégées par la surveillance peut entraîner un retard important au démarrage de l'application.
  • Transactions de liaison synchrones: recherchez les transactions inutiles dans le chemin critique de votre application. Si une transaction nécessaire est coûteuse, envisagez de collaborer avec l'équipe chargée de la plate-forme associée pour l'améliorer.

  • Récupération de mémoire simultanée: ceci est courant et a un impact relativement faible, mais si vous en rencontrez souvent, envisagez de l'examiner avec le profileur de mémoire Android Studio.

  • E/S: vérifiez si les E/S ont été effectuées au démarrage et recherchez les longs blocages.

  • Activité importante sur les autres threads: cela peut interférer avec le thread UI. Faites attention aux tâches en arrière-plan au démarrage.

Nous vous recommandons d'appeler reportFullyDrawn lorsque le démarrage est terminé du point de vue de l'application afin d'améliorer les rapports sur les métriques de démarrage de l'application. Consultez la section Délai d'affichage total pour plus d'informations sur l'utilisation de reportFullyDrawn. Vous pouvez extraire les heures de début définies par RFD via le processeur de traces Perfetto, et un événement de trace visible par l'utilisateur est émis.

Utiliser le traçage système sur l'appareil

Vous pouvez utiliser l'application "Traçage système" au niveau du système pour capturer une trace système sur un appareil. Cette application vous permet d'enregistrer des traces à partir de l'appareil sans avoir à le brancher ni à le connecter à adb.

Utiliser le Profileur de mémoire Android Studio

Vous pouvez utiliser le Profileur de mémoire d'Android Studio pour inspecter la pression sur la mémoire qui peut être causée par des fuites de mémoire ou des modèles d'utilisation incorrects. Elle offre une vue en direct des allocations d'objets.

Vous pouvez résoudre les problèmes de mémoire dans votre application en suivant les informations liées à l'utilisation du Profileur de mémoire pour savoir pourquoi et à quelle fréquence les récupérations de mémoire se produisent.

Pour profiler la mémoire de l'application, procédez comme suit:

  1. Détectez les problèmes de mémoire.

    Enregistrez une session de profilage de mémoire du parcours utilisateur sur lequel vous souhaitez vous concentrer. Recherchez si le nombre d'objets augmente, comme illustré dans la figure 7, ce qui conduit à terme à des récupérations de mémoire, comme illustré dans la figure 8.

    alt_text Figure 7. Augmentation du nombre d'objets.

    alt_text Figure 8. Récupération de mémoire

    Une fois que vous avez identifié le parcours utilisateur qui augmente la pression sur la mémoire, analysez les causes fondamentales de cette pression.

  2. Diagnostiquer les zones sensibles de pression sur la mémoire.

    Sélectionnez une plage dans la chronologie pour visualiser à la fois les allocations et la taille superficielle, comme illustré dans la figure 9.

    alt_text Figure 9. Valeurs pour les allocations et la taille superficielle.

    Il existe plusieurs façons de trier ces données. Voici quelques exemples de la manière dont chaque vue peut vous aider à analyser les problèmes.

    • Classer par classe: utile lorsque vous souhaitez rechercher des classes qui génèrent des objets qui sont autrement mis en cache ou réutilisés à partir d'un pool de mémoire.

      Par exemple, si vous voyez qu'une application crée 2 000 objets de classe appelée "Vertex" chaque seconde, le nombre d'allocations augmente de 2 000 toutes les secondes, ce qui s'affiche lors du tri par classe. Si vous souhaitez réutiliser ces objets pour éviter de générer de la mémoire, implémentez un pool de mémoire.

    • Organisation par pile d'appels: utile lorsque vous souhaitez savoir où se trouve un chemin réactif dans lequel la mémoire est allouée, par exemple dans une boucle ou dans une fonction spécifique effectuant beaucoup de travail d'allocation.

    • Taille superficielle: suit uniquement la mémoire de l'objet lui-même. Il est utile pour suivre des classes simples composées principalement de valeurs primitives uniquement.

    • Taille conservée: affiche la mémoire totale en raison de l'objet, ainsi que les références uniquement référencées par l'objet. Il est utile pour suivre la pression sur la mémoire en raison d'objets complexes. Pour obtenir cette valeur, effectuez une vidage de mémoire complet, comme illustré dans la figure 10. La valeur Taille conservée est ajoutée en tant que colonne, comme illustré dans la figure 11.

      alt_text Figure 10 : Dump mémoire complet.

      Taille conservée.
      Figure 11. colonne "Taille conservée".
  3. Mesurez l'impact d'une optimisation.

    Les récupérations de mémoire sont plus évidentes et plus faciles à mesurer l'impact de l'optimisation de la mémoire. Lorsqu'une optimisation réduit la pression sur la mémoire, le nombre de récupérations de mémoire est moins élevé.

    Pour mesurer l'impact de l'optimisation, mesurez le délai entre les récupérations de mémoire dans la chronologie du profileur. Vous pouvez ainsi constater que le délai entre deux récupérations de mémoire est plus long.

    Les améliorations ultimes des améliorations de la mémoire sont les suivantes:

    • Les arrêts pour mémoire insuffisante sont probablement réduits si l'application n'affecte pas constamment la pression de la mémoire.
    • Avoir moins de récupération de mémoire améliore les métriques d'à-coups, en particulier sur la P99. En effet, les récupérations de mémoire provoquent des conflits au niveau du processeur, ce qui peut différer les tâches de rendu.