Évaluer les performances

Utilisez Simpleperf pour évaluer les performances d'un appareil. Simpleperf est un outil de profilage natif pour les applications et les processus natifs sur Android. Utilisez le Profileur de processeur pour inspecter l'utilisation du processeur et l'activité des threads de l'application en temps réel.

Deux indicateurs de performances sont visibles par les utilisateurs :

  • Performances prévisibles et perceptibles. L'interface utilisateur (UI) perd-elle des frames ou s'affiche-t-elle de manière constante à 60 FPS ? Le contenu audio est-il lu sans artefacts ni grésillements ? Quel est le délai entre le moment où l'utilisateur touche l'écran et le moment où l'effet s'affiche ?
  • Durée requise pour les opérations plus longues (comme l'ouverture d'applications).

Le premier est plus visible que le second. Les utilisateurs remarquent généralement les à-coups, mais ils ne peuvent pas faire la différence entre un temps de démarrage de l'application de 500 ms ou de 600 ms, sauf s'ils regardent deux appareils côte à côte. La latence tactile est immédiatement perceptible et contribue de manière significative à la perception d'un appareil.

Par conséquent, sur un appareil rapide, le pipeline d'UI est l'élément le plus important du système, à l'exception de ce qui est nécessaire pour maintenir le pipeline d'UI fonctionnel. Cela signifie que le pipeline d'UI (interface utilisateur) doit préempter toute autre tâche non nécessaire pour assurer la fluidité de l'UI. Pour maintenir une UI fluide, la synchronisation en arrière-plan, la distribution des notifications et les tâches similaires doivent toutes être retardées afin que l'UI puisse être exécutée. Il est acceptable de sacrifier les performances d'opérations plus longues (durée d'exécution HDR+, démarrage d'application, etc.) pour assurer la fluidité de l'UI.

Capacité et gigue

Lorsque vous examinez les performances des appareils, la capacité et la gigue sont deux métriques importantes.

Capacité

La capacité correspond à la quantité totale d'une ressource dont dispose l'appareil pendant une certaine période. Il peut s'agir de ressources de processeur, de GPU, d'E/S ou de réseau, de bande passante mémoire ou de toute autre métrique similaire. Lors de l'examen des performances de l'ensemble d'un système, il peut être utile de faire abstraction des composants individuels et de prendre en compte une seule métrique déterminant les performances (en particulier lors de l'optimisation d'un nouvel appareil, car les charges de travail exécutées sur cet appareil sont probablement fixes).

La capacité d'un système varie en fonction des ressources de calcul en ligne. La modification de la fréquence du processeur/GPU est le principal moyen de modifier la capacité, mais il en existe d'autres, comme la modification du nombre de cœurs de processeur en ligne. Par conséquent, la capacité d'un système correspond à la consommation d'énergie. Toute modification de la capacité entraîne toujours une modification similaire de la consommation d'énergie.

La capacité requise à un moment donné est principalement déterminée par l'application en cours d'exécution. Par conséquent, la plate-forme ne peut pas faire grand-chose pour ajuster la capacité requise pour une charge de travail donnée. Les moyens d'y parvenir se limitent aux améliorations du temps d'exécution (framework Android, ART, Bionic, compilateur/pilotes GPU, kernel).

Gigue

Bien que la capacité requise pour une charge de travail soit facile à identifier, la gigue est un concept plus nébuleux. Pour une bonne introduction à la gigue, synonyme d'obstacle à la rapidité des systèmes, nous vous recommandons de lire le document intitulé The Case of the Missing Supercomputer Performance: Achieving Optimal Performance on the 8,192 processors of ASCI Q. (Il s'agit d'une étude sur les raisons pour lesquelles le superordinateur ASCI Q n'a pas atteint les performances attendues. C'est une excellente introduction à l'optimisation des grands systèmes.)

Cette page utilise le terme "gigue" pour décrire ce que le document ASCI Q appelle bruit. La gigue est le comportement aléatoire du système qui empêche l'exécution de tâches perceptibles. Il s'agit souvent de tâches qui doivent être exécutées, mais qui peuvent ne pas avoir d'exigences de timing strictes qui les obligent à s'exécuter à un moment précis. Comme elle est aléatoire, il est extrêmement difficile de réfuter l'existence de gigue pour une charge de travail donnée. Il est également extrêmement difficile de prouver qu'une source connue de gigue était à l'origine d'un problème de performances particulier. Les outils les plus couramment utilisés pour diagnostiquer les causes de gigue (tels que le traçage ou la journalisation) peuvent introduire leur propre gigue.

Voici quelques sources de gigue rencontrées dans des implémentations réelles d'Android :

  • Retard du programmeur
  • Gestionnaires d'interruptions
  • Code du pilote s'exécutant trop longtemps avec la préemption ou les interruptions désactivées
  • Softirqs de longue durée
  • Conflit de verrouillage (application, framework, pilote du kernel, verrouillage Binder, verrouillage mmap)
  • Conflit de descripteur de fichier où un thread de faible priorité détient le verrou sur un fichier, empêchant l'exécution d'un thread de haute priorité
  • Exécution de code essentiel à l'UI dans des files d'attente de tâches où il pourrait être retardé
  • Transitions d'inactivité du processeur
  • Journalisation
  • Retards d'E/S
  • Création de processus inutiles (par exemple, diffusions CONNECTIVITY_CHANGE)
  • Thrashing du cache de page causé par une mémoire libre insuffisante

La durée requise pour une période de gigue donnée peut ou non diminuer à mesure que la capacité augmente. Par exemple, si un pilote laisse les interruptions désactivées en attendant une lecture sur un bus i2c, cette durée sera fixe, que le processeur soit à 384 MHz ou à 2 GHz. Augmenter la capacité n'est pas une solution viable pour améliorer les performances en cas de gigue. Par conséquent, des processeurs plus rapides n'améliorent généralement pas les performances dans les situations où la gigue est limitée.

Enfin, contrairement à la capacité, la gigue relève presque entièrement du fournisseur du système.

Consommation de mémoire

La consommation de mémoire est traditionnellement considérée comme responsable des mauvaises performances. Bien que la consommation en elle-même ne soit pas un problème de performances, elle peut entraîner une gigue en raison de la surcharge de lowmemorykiller, des redémarrages de service et du thrashing du cache de page. La réduction de la consommation de mémoire peut éviter les causes directes de mauvaises performances, mais il peut exister d'autres améliorations ciblées qui évitent également ces causes (par exemple, l'épinglage du framework pour l'empêcher d'être paginé lorsqu'il le sera peu de temps après).

Analyser les performances initiales de l'appareil

Partir d'un système fonctionnel, mais peu performant, et tenter de corriger son comportement en examinant des cas individuels de performances médiocres visibles par l'utilisateur n'est pas une stratégie judicieuse. Étant donné que les mauvaises performances sont généralement difficiles à reproduire (c'est-à-dire, la gigue) ou qu'il s'agit d'un problème lié aux applications, le trop grand nombre de variables dans le système complet empêche cette stratégie d'être efficace. Par conséquent, il est très facile d'identifier les causes de manière incorrecte et d'apporter des améliorations mineures tout en passant à côté d'opportunités systémiques qui auraient permis d'améliorer les performances dans l'ensemble du système.

Utilisez plutôt l'approche générale suivante lorsque vous configurez un nouvel appareil :

  1. Démarrez le système sur l'UI avec tous les pilotes en cours d'exécution et certains paramètres de base du gouverneur de fréquence (si vous modifiez les paramètres du gouverneur de fréquence, répétez toutes les étapes ci-dessous).
  2. Assurez-vous que le kernel prend en charge le point de trace sched_blocked_reason, ainsi que d'autres points de trace dans le pipeline d'affichage qui indiquent quand le frame est transmis à l'écran.
  3. Effectuez de longues traces de l'ensemble du pipeline d'UI (de la réception de l'entrée par le biais d'une IRQ au scanout final) tout en exécutant une charge de travail légère et cohérente (par exemple, UiBench ou le test de la balle dans TouchLatency).
  4. Corrigez les pertes de frames détectées dans la charge de travail légère et cohérente.
  5. Répétez les étapes 3 et 4 jusqu'à ce que vous puissiez effectuer une exécution sans perte de frames pendant au moins 20 secondes.
  6. Passez à d'autres sources d'à-coups visibles par l'utilisateur.

Voici d'autres actions simples que vous pouvez effectuer au début de la mise en service de l'appareil :

  • Assurez-vous que votre kernel dispose du correctif de point de trace sched_blocked_reason. Ce point de trace est activé avec la catégorie de trace sched dans systrace et fournit la fonction responsable de la mise en veille lorsque ce thread entre en veille non interruptible. Cela est essentiel pour l'analyse des performances, car le sommeil ininterrompu est un indicateur de gigue très courant.
  • Assurez-vous de disposer d'un traçage suffisant pour les pipelines GPU et d'affichage. Sur les SoC Qualcomm récents, les points de trace sont activés à l'aide de :
  • adb shell "echo 1 > /d/tracing/events/kgsl/enable"
    adb shell "echo 1 > /d/tracing/events/mdss/enable"
    

    Ces événements restent activés lorsque vous exécutez systrace. Vous pouvez ainsi afficher des informations supplémentaires dans la trace concernant le pipeline d'affichage (MDSS) dans la section mdss_fb0. Sur les SoC Qualcomm, vous ne verrez aucune information supplémentaire sur le GPU dans la vue systrace standard, mais les résultats sont présents dans la trace elle-même (pour en savoir plus, consultez Comprendre systrace).

    Ce que vous attendez de ce type de traçage d'affichage, c'est un événement unique qui indique directement qu'un frame a été envoyé à l'écran. Vous pouvez alors déterminer si vous avez atteint votre temps de rendu avec succès. Si l'événement Xn se produit moins de 16,7 ms après l'événement Xn-1 (en supposant un écran de 60 Hz), vous savez que vous n'avez pas d'à-coup. Si votre SOC ne fournit pas de tels signaux, contactez votre fournisseur pour les obtenir. Il est extrêmement difficile de déboguer la gigue sans signal définitif de fin de frame.

Utiliser des benchmarks synthétiques

Les benchmarks synthétiques sont utiles pour s'assurer que les fonctionnalités de base d'un appareil sont présentes. Toutefois, il n'est pas utile de traiter les benchmarks comme un proxy pour les performances perçues de l'appareil.

D'après les expériences avec les SoC, les différences de performances des benchmarks synthétiques entre les SoC ne sont pas corrélées à une différence similaire dans les performances perceptibles de l'UI (nombre de pertes de frames, temps de rendu au 99e centile, etc.). Les benchmarks synthétiques sont des benchmarks de capacité uniquement. La gigue n'a d'impact sur les performances mesurées de ces benchmarks qu'en "volant" du temps au fonctionnement groupé du benchmark. Par conséquent, les scores de benchmark synthétiques sont pour la plupart sans intérêt en tant que métrique des performances perçues par les utilisateurs.

Prenons l'exemple de deux SoC exécutant Benchmark X, qui affiche 1 000 frames d'UI et indique le temps de rendu total (un score plus faible est préférable).

  • SOC 1 affiche chaque frame de Benchmark X en 10 ms et obtient un score de 10 000.
  • SOC 2 affiche 99 % des frames en 1 ms, mais 1 % des frames en 100 ms et obtient un score de 19 900, ce qui est nettement mieux.

Si le benchmark est représentatif des performances réelles de l'UI, SOC 2 serait inutilisable. En supposant une fréquence d'actualisation de 60 Hz, SOC 2 aurait un frame saccadé toutes les 1,5 s de fonctionnement. Pendant ce temps, le SOC 1 (le SOC le plus lent selon Benchmark X) serait parfaitement fluide.

Utiliser les rapports de bug

Les rapports de bug sont parfois utiles pour l'analyse des performances, mais comme ils sont très lourds, ils sont rarement utiles pour déboguer les problèmes d'à-coups sporadiques. Ils peuvent fournir des indices sur ce que le système faisait à un moment donné, en particulier si l'à-coup s'est produit lors d'une transition d'application (qui est enregistré dans un rapport de bug). Les rapports de bug peuvent également indiquer si un problème plus général affecte le système et pourrait réduire sa capacité effective (par exemple, la limitation thermique ou la fragmentation de la mémoire).

Utiliser TouchLatency

Plusieurs exemples de mauvais comportements proviennent de TouchLatency, la charge de travail périodique préférée qui est utilisée pour les Pixel et Pixel XL. TouchLatency est disponible sur frameworks/base/tests/TouchLatency et comporte deux modes : latence tactile et balle rebondissante (pour changer de mode, cliquez sur le bouton en haut à droite).

Le test de la balle rebondissante est très simple : une balle rebondit indéfiniment sur l'écran, quelle que soit l'action de l'utilisateur. C'est généralement le test le plus difficile à exécuter parfaitement, mais plus il s'approche d'une exécution sans perte de frames, plus votre appareil sera performant. Le test de la balle rebondissante est difficile, car il s'agit d'une charge de travail triviale, mais parfaitement cohérente, qui s'exécute à une fréquence d'horloge très basse (cela suppose que l'appareil dispose d'un gouverneur de fréquence ; si l'appareil fonctionne plutôt avec des fréquences d'horloge fixes, réduisez la fréquence du processeur/GPU à une valeur proche du minimum lorsque vous exécutez le test de la balle rebondissante pour la première fois). À mesure que le système se calme et que les fréquences d'horloge se rapprochent de l'état inactif, le temps de processeur/GPU requis par frame augmente. Vous pouvez observer la balle et voir les à-coups, et vous pourrez également voir les frames manqués dans systrace.

Étant donné que la charge de travail est très cohérente, vous pouvez identifier la plupart des sources de gigue beaucoup plus facilement que dans la plupart des charges de travail visibles par l'utilisateur. Pour ce faire, suivez exactement ce qui s'exécute sur le système pendant chaque frame manquant au lieu du pipeline d'UI. Les fréquences d'horloge plus basses amplifient les effets de la gigue, car il est plus probable que toute gigue entraîne une perte de frame. Par conséquent, plus TouchLatency est proche de 60 FPS, moins vous risquez de rencontrer des comportements système indésirables qui provoquent des à-coups sporadiques et difficiles à reproduire dans les applications plus volumineuses.

Comme la gigue est souvent (mais pas toujours) invariant à la fréquence d'horloge, utilisez un test qui s'exécute à des fréquences d'horloge très basses pour diagnostiquer la gigue pour les raisons suivantes :

  • Toutes les gigues ne sont pas invariantes à la fréquence d'horloge. De nombreuses sources consomment simplement du temps de processeur.
  • Le gouverneur devrait faire en sorte que le temps de rendu moyen soit proche du délai en réduisant la fréquence d'horloge. Le temps passé à exécuter des tâches non liées à l'UI peut donc lui faire dépasser la limite et entraîner la perte d'un frame.