Exemples de mesure et d'analyse des performances

Ces exemples montrent comment utiliser le traçage système avec Macrobenchmark, ainsi que la création de profils de mémoire pour mesurer et améliorer certains types de problèmes de performances.

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

Lors du débogage du temps de démarrage, nous vous recommandons d'utiliser les journaux Systrace. Systrace est un système qui s'appuie sur du code préinstrumenté pour indiquer le temps que prennent certains événements. Ces traces vous permettent de voir ce qui se passe dans votre application, ou même dans d'autres processus du système. La plate-forme Android et les bibliothèques Jetpack sont associées à de nombreux événements clés dans une application, qui sont consignées en conséquence. Vous pouvez également instrumenter vos applications avec vos propres traces personnalisées, qui apparaîtront dans les mêmes outils de visualisation Systrace, afin d'obtenir une vision globale de ce qui s'est passé dans l'application.

Utiliser Systrace ou Perfetto

Pour en savoir plus sur l'utilisation de base de Systrace, regardez la vidéo suivante : Debugging Application Performance (Déboguer les performances de l'application).

Pour analyser le temps de démarrage, vous devez d'abord comprendre ce qui se passe pendant le démarrage. Pour obtenir plus d'informations que ce qui est décrit sur cette page, la documentation sur le temps de démarrage de l'application donne un aperçu du processus de démarrage de l'application.

Les étapes de démarrage de l'application sont les suivantes :

  • Lancer le processus
  • Initialiser des objets d'application génériques
  • Créer et initialiser une activité
  • Gonfler la mise en page
  • Dessiner la première image

Les types de démarrage comportent les étapes suivantes :

  • Démarrage à froid : se produit lorsque l'application est lancée pour la première fois depuis le démarrage, ou lorsque le processus de l'application a été arrêté par l'utilisateur ou par le système. Le démarrage crée un nouveau processus sans état enregistré.
  • Démarrage tiède : se produit lorsque l'application s'exécute déjà en arrière-plan, mais que l'activité doit être recréée et passée au premier plan. L'activité est recréée lorsque vous réutilisez le processus existant, ou le processus est recréé avec l'état enregistré. La bibliothèque de tests Macrobenchmark accepte les tests de démarrage tiède cohérents à l'aide de la première option.
  • Démarrage à chaud : lorsque le processus et l'activité sont toujours en cours d'exécution et qu'il suffit de les mettre au premier plan, en recréant éventuellement des objets si nécessaire, et en affichant la nouvelle activité au premier plan. Il s'agit du scénario de démarrage le plus court.

Nous vous recommandons de capturer les systraces à l'aide de l'application de traçage système sur l'appareil disponible dans les Options pour les développeurs. Si vous souhaitez utiliser des outils de ligne de commande, Perfetto est disponible pour Android 10 (niveau d'API 29) ou version ultérieure. Les appareils équipés de versions antérieures doivent utiliser systrace.

Notez que l'expression "première image" est un peu trompeuse, car les applications peuvent varier de manière significative en termes de gestion du démarrage après la création de l'activité initiale. Certaines applications continuent à gonfler plusieurs images, tandis que d'autres se lancent immédiatement dans une activité secondaire.

Dans la mesure du possible, nous vous recommandons d'inclure un appel reportFullyDrawn (disponible sur Android 10 ou version ultérieure) lorsque le démarrage est effectué du point de vue de l'application.

Voici quelques éléments à rechercher dans ces traces système :

Contrôle des conflits
Figure 1 : La concurrence pour les ressources protégées via des contrôles peut entraîner un décalage important au démarrage de l'application.

Transactions de liaisons synchrones
Figure 2 : Recherchez les transactions inutiles dans le chemin critique de votre application.

Récupération de mémoire simultanée
Figure 3 : La récupération de mémoire simultanée est courante et a un impact relativement faible. Toutefois, si vous la sollicitez trop souvent, nous vous recommandons d'étudier la question à l'aide du Profileur de mémoire Android Studio.

E/S au démarrage
Figure 4 : Recherchez les E/S pendant le démarrage et recherchez les blocages.

Avec la figure 4, notez que d'autres processus effectuant des E/S en même temps peuvent entraîner des conflits d'E/S. Vérifiez donc qu'il n'y a pas d'autres processus qui s'exécutent.

Une activité significative sur d'autres threads peut interférer avec le thread UI. Par conséquent, faites attention au travail en arrière-plan au démarrage. Notez que les appareils peuvent avoir différentes configurations de processeur. Le nombre de threads pouvant s'exécuter en parallèle peut donc varier d'un appareil à un autre.

Consultez également le guide sur les causes courantes d'à-coups.

Utiliser le Profileur de mémoire Android Studio

Le Profileur de mémoire Android Studio est un outil puissant conçu pour réduire la pression sur la mémoire qui peut être due à des fuites de mémoire ou à des modèles d'utilisation incorrects. Il offre une vue en direct des allocations et des collections d'objets.

Pour résoudre les problèmes de mémoire de votre application, vous pouvez utiliser le Profileur de mémoire afin de savoir pourquoi et à quelle fréquence les récupérations de mémoire se produisent, et s'il existe des fuites de mémoire qui entraînent l'augmentation constante de votre tas de mémoire au fil du temps.

Le profilage de la mémoire de l'application se déroule comme suit :

1. Détecter des problèmes de mémoire

Pour détecter les problèmes de mémoire, commencez par enregistrer une session de profilage de mémoire pour votre application. Recherchez ensuite un objet dont l'empreinte mémoire augmente, ce qui déclenche à terme un événement de récupération de mémoire.

Augmentation du nombre d'objets
Figure 5 : Le Profileur de mémoire affiche une augmentation des allocations d'objets au fil du temps.

Récupérations de mémoire
Figure 6 : Profileur de mémoire affichant les événements de récupération de mémoire.{.:image-caption}

Une fois que vous avez identifié un cas d'utilisation qui augmente la pression sur la mémoire, commencez à analyser les causes.

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

Sélectionnez une plage dans la chronologie pour visualiser les allocations et la taille superficielle.

Visualiser les allocations et la taille superficielle
Figure 7 : Profileur de mémoire affichant les allocations et les tailles d'une plage sélectionnée dans la chronologie.

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

Organiser par classe

L'organisation par classe est utile lorsque vous souhaitez rechercher des classes qui génèrent des objets qui, autrement, devraient être mis en cache ou réutilisés à partir d'un pool de mémoire.

Par exemple, imaginez qu'une application crée 2 000 objets d'une classe appelée "Vertex" par seconde. Cela augmenterait le nombre d'allocations de 2 000 par seconde, ce qui serait visible lors du tri par classe. Ces objets doivent-ils être réutilisés pour éviter ce gaspillage ? Si la réponse est "Oui", vous devrez probablement implémenter un pool de mémoire.

Organiser par pile d'appels

L'organisation par pile d'appels est utile lorsqu'il y a un chemin réactif dans lequel de la mémoire est attribuée, par exemple dans une boucle ou dans une fonction spécifique effectuant de nombreuses opérations d'allocation. L'affichage par pile d'appels vous permet de voir ces hotspots d'allocation.

Taille superficielle ou conservée

La taille superficielle ne suit que la mémoire de l'objet. Elle est donc particulièrement utile pour suivre des classes simples composées principalement de primitives.

La taille conservée affiche la mémoire totale allouée directement par l'objet, ainsi que les autres objets alloués qui ne sont référencés que par l'objet. Elle est utile pour suivre la pression sur la mémoire en raison d'objets complexes qui nécessitent l'allocation d'autres objets, et pas seulement des champs primitifs. Pour obtenir cette valeur, créez un vidage de mémoire à l'aide du Profileur de mémoire. Les objets alloués dans ce tas de mémoire sont ajoutés à l'écran.

Vidage complet de la mémoire
Figure 8 : Vous pouvez créer un vidage de mémoire à tout moment en cliquant sur le bouton de vidage du segment de mémoire Java dans la barre d'outils du Profileur de mémoire.

ajouté dans une colonne
Figure 9 : Lors de la création d'un vidage de mémoire, une colonne affiche les allocations d'objets dans ce tas de mémoire.

3. Mesurer l'impact d'une optimisation

L'une des améliorations de l'optimisation de la mémoire qui est facile à mesurer est la récupération de mémoire. Lorsqu'une optimisation réduit la pression sur la mémoire, vous devriez constater une réduction du nombre de récupérations de mémoire. Pour mesurer cela, mesurez le délai entre les récupérations de mémoire dans la chronologie du profileur. Les durées entre les récupérations de mémoire doivent être plus élevées après les optimisations de mémoire.

L'impact final des améliorations de la mémoire est le suivant :

  • L'application se fermera moins souvent en raison de problèmes de mémoire insuffisante si elle n'est pas constamment sollicitée.
  • Avoir moins de récupérations de mémoire améliore les métriques liées aux à-coups. 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.