Android 8.0 inclut des tests de performances de binder et hwbinder pour le débit et la latence. Bien qu'il existe de nombreux scénarios permettant de détecter des problèmes de performances perceptibles, leur exécution peut prendre du temps et les résultats ne sont souvent disponibles qu'après l'intégration du système. L'utilisation des tests de performances fournis facilite les tests pendant le développement, détecte plus tôt les problèmes graves et améliore l'expérience utilisateur.
Les tests de performances comprennent les quatre catégories suivantes :
- débit du classeur (disponible dans
system/libhwbinder/vts/performance/Benchmark_binder.cpp
) - latence du classeur (disponible dans
frameworks/native/libs/binder/tests/schd-dbg.cpp
) - Débit hwbinder (disponible dans
system/libhwbinder/vts/performance/Benchmark.cpp
) - Latence hwbinder (disponible dans
system/libhwbinder/vts/performance/Latency.cpp
)
À propos du classeur et du hwbinder
Binder et hwbinder sont des infrastructures de communication inter-processus (IPC) Android qui partagent le même pilote Linux mais présentent les différences qualitatives suivantes :
Aspect | classeur | classeur |
---|---|---|
But | Fournir un schéma IPC à usage général pour le cadre | Communiquer avec le matériel |
Propriété | Optimisé pour l'utilisation du framework Android | Faible latence minimale |
Modifier la politique de planification pour le premier plan/arrière-plan | Oui | Non |
Arguments passant | Utilise la sérialisation prise en charge par l'objet Parcel | Utilise des tampons de dispersion et évite la surcharge liée à la copie des données requises pour la sérialisation des colis |
Héritage prioritaire | Non | Oui |
Processus de liant et de hwbinder
Un visualiseur systrace affiche les transactions comme suit :
Dans l'exemple ci-dessus :
- Les quatre (4) processus schd-dbg sont des processus clients.
- Les quatre (4) processus Binder sont des processus serveur (le nom commence par Binder et se termine par un numéro de séquence).
- Un processus client est toujours associé à un processus serveur, dédié à son client.
- Toutes les paires de processus client-serveur sont planifiées indépendamment par le noyau simultanément.
Dans le CPU 1, le noyau du système d'exploitation exécute le client pour émettre la requête. Il utilise ensuite le même processeur autant que possible pour réveiller un processus serveur, gérer la requête et revenir en arrière une fois la requête terminée.
Débit et latence
Dans une transaction parfaite, où les processus client et serveur commutent de manière transparente, les tests de débit et de latence ne produisent pas de messages sensiblement différents. Cependant, lorsque le noyau du système d'exploitation gère une demande d'interruption (IRQ) provenant du matériel, attend des verrous ou choisit simplement de ne pas gérer un message immédiatement, une bulle de latence peut se former.
Le test de débit génère un grand nombre de transactions avec différentes tailles de charge utile, fournissant une bonne estimation du temps de transaction régulier (dans le meilleur des cas) et du débit maximum que le classeur peut atteindre.
En revanche, le test de latence n’effectue aucune action sur la charge utile afin de minimiser le temps de transaction normal. Nous pouvons utiliser le temps de transaction pour estimer la surcharge du classeur, établir des statistiques pour le pire des cas et calculer le ratio de transactions dont la latence respecte un délai spécifié.
Gérer les inversions de priorité
Une inversion de priorité se produit lorsqu'un thread de priorité plus élevée attend logiquement un thread de priorité inférieure. Les applications temps réel (RT) ont un problème d'inversion de priorité :
Lorsque vous utilisez la planification Linux Completely Fair Scheduler (CFS), un thread a toujours une chance de s'exécuter même lorsque d'autres threads ont une priorité plus élevée. En conséquence, les applications avec planification CFS gèrent l'inversion de priorité comme un comportement attendu et non comme un problème. Dans les cas où le framework Android a besoin d'une planification RT pour garantir le privilège des threads haute priorité, l'inversion de priorité doit être résolue.
Exemple d'inversion de priorité lors d'une transaction de classeur (le thread RT est logiquement bloqué par d'autres threads CFS lors de l'attente du service d'un thread de classeur) :
Pour éviter les blocages, vous pouvez utiliser l'héritage de priorité pour escalader temporairement le thread Binder vers un thread RT lorsqu'il répond à une demande d'un client RT. Gardez à l’esprit que la planification RT dispose de ressources limitées et doit être utilisée avec prudence. Dans un système avec n processeurs, le nombre maximum de threads RT actuels est également n ; des threads RT supplémentaires pourraient devoir attendre (et donc manquer leurs délais) si tous les processeurs sont occupés par d'autres threads RT.
Pour résoudre toutes les inversions de priorité possibles, vous pouvez utiliser l'héritage de priorité pour binder et hwbinder. Cependant, comme le classeur est largement utilisé dans le système, l'activation de l'héritage prioritaire pour les transactions du classeur peut spammer le système avec plus de threads RT qu'il ne peut en traiter.
Exécuter des tests de débit
Le test de débit est exécuté sur le débit des transactions binder/hwbinder. Dans un système qui n’est pas surchargé, les bulles de latence sont rares et leur impact peut être éliminé à condition que le nombre d’itérations soit suffisamment élevé.
- Le test de débit du classeur se trouve dans
system/libhwbinder/vts/performance/Benchmark_binder.cpp
. - Le test de débit hwbinder se trouve dans
system/libhwbinder/vts/performance/Benchmark.cpp
.
Résultats de test
Exemples de résultats de tests de débit pour des transactions utilisant différentes tailles de charge utile :
Benchmark Time CPU Iterations --------------------------------------------------------------------- BM_sendVec_binderize/4 70302 ns 32820 ns 21054 BM_sendVec_binderize/8 69974 ns 32700 ns 21296 BM_sendVec_binderize/16 70079 ns 32750 ns 21365 BM_sendVec_binderize/32 69907 ns 32686 ns 21310 BM_sendVec_binderize/64 70338 ns 32810 ns 21398 BM_sendVec_binderize/128 70012 ns 32768 ns 21377 BM_sendVec_binderize/256 69836 ns 32740 ns 21329 BM_sendVec_binderize/512 69986 ns 32830 ns 21296 BM_sendVec_binderize/1024 69714 ns 32757 ns 21319 BM_sendVec_binderize/2k 75002 ns 34520 ns 20305 BM_sendVec_binderize/4k 81955 ns 39116 ns 17895 BM_sendVec_binderize/8k 95316 ns 45710 ns 15350 BM_sendVec_binderize/16k 112751 ns 54417 ns 12679 BM_sendVec_binderize/32k 146642 ns 71339 ns 9901 BM_sendVec_binderize/64k 214796 ns 104665 ns 6495
- Le temps indique le délai aller-retour mesuré en temps réel.
- CPU indique le temps accumulé pendant lequel les CPU sont planifiés pour le test.
- Les itérations indiquent le nombre de fois où la fonction de test a été exécutée.
Par exemple, pour une charge utile de 8 octets :
BM_sendVec_binderize/8 69974 ns 32700 ns 21296
… le débit maximum que le liant peut atteindre est calculé comme suit :
Débit MAX avec charge utile de 8 octets = (8 * 21296)/69974 ~= 2,423 b/ns ~= 2,268 Gb/s
Options de test
Pour obtenir les résultats au format .json, exécutez le test avec l'argument --benchmark_format=json
:
libhwbinder_benchmark --benchmark_format=json
{
"context": {
"date": "2017-05-17 08:32:47",
"num_cpus": 4,
"mhz_per_cpu": 19,
"cpu_scaling_enabled": true,
"library_build_type": "release"
},
"benchmarks": [
{
"name": "BM_sendVec_binderize/4",
"iterations": 32342,
"real_time": 47809,
"cpu_time": 21906,
"time_unit": "ns"
},
….
}
Exécuter des tests de latence
Le test de latence mesure le temps nécessaire au client pour commencer à initialiser la transaction, passer au processus serveur pour le traitement et recevoir le résultat. Le test recherche également les mauvais comportements connus du planificateur qui peuvent avoir un impact négatif sur la latence des transactions, comme un planificateur qui ne prend pas en charge l'héritage de priorité ou qui ne respecte pas l'indicateur de synchronisation.
- Le test de latence du classeur se trouve dans
frameworks/native/libs/binder/tests/schd-dbg.cpp
. - Le test de latence hwbinder se trouve dans
system/libhwbinder/vts/performance/Latency.cpp
.
Résultats de test
Les résultats (au format .json) affichent des statistiques sur la latence moyenne/meilleure/pire et le nombre de délais manqués.
Options de test
Les tests de latence prennent les options suivantes :
Commande | Description |
---|---|
-i value | Spécifiez le nombre d'itérations. |
-pair value | Spécifiez le nombre de paires de processus. |
-deadline_us 2500 | Précisez-nous le délai. |
-v | Obtenez une sortie détaillée (débogage). |
-trace | Arrêtez la trace en cas de dépassement d'une date limite. |
Les sections suivantes détaillent chaque option, décrivent leur utilisation et fournissent des exemples de résultats.
Spécifier les itérations
Exemple avec un grand nombre d'itérations et une sortie détaillée désactivée :
libhwbinder_latency -i 5000 -pair 3
{
"cfg":{"pair":3,"iterations":5000,"deadline_us":2500},
"P0":{"SYNC":"GOOD","S":9352,"I":10000,"R":0.9352,
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996},
"fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
},
"P1":{"SYNC":"GOOD","S":9334,"I":10000,"R":0.9334,
"other_ms":{ "avg":0.19, "wst":2.9 , "bst":0.055, "miss":2, "meetR":0.9996},
"fifo_ms": { "avg":0.16, "wst":3.1 , "bst":0.066, "miss":1, "meetR":0.9998}
},
"P2":{"SYNC":"GOOD","S":9369,"I":10000,"R":0.9369,
"other_ms":{ "avg":0.19, "wst":4.8 , "bst":0.055, "miss":6, "meetR":0.9988},
"fifo_ms": { "avg":0.15, "wst":1.8 , "bst":0.067, "miss":0, "meetR":1}
},
"inheritance": "PASS"
}
Ces résultats de tests montrent ce qui suit :
-
"pair":3
- Crée une paire client et serveur.
-
"iterations": 5000
- Comprend 5 000 itérations.
-
"deadline_us":2500
- Le délai est de 2 500 us (2,5 ms) ; la plupart des transactions devraient atteindre cette valeur.
-
"I": 10000
- Une seule itération de test comprend deux (2) transactions :
- Une transaction par priorité normale (
CFS other
) - Une transaction par priorité temps réel (
RT-fifo
)
- Une transaction par priorité normale (
-
"S": 9352
- 9 352 transactions sont synchronisées dans le même processeur.
-
"R": 0.9352
- Indique le taux de synchronisation du client et du serveur dans le même processeur.
-
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996}
- La moyenne (
avg
), le pire (wst
) et le meilleur (bst
) pour toutes les transactions émises par un appelant prioritaire normal. Deux transactionsmiss
la date limite, ce qui porte le ratio de rencontre (meetR
) à 0,9996. -
"fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
- Similaire à
other_ms
, mais pour les transactions émises par le client avec la prioritért_fifo
. Il est probable (mais pas obligatoire) que lefifo_ms
ait un meilleur résultat queother_ms
, avec des valeursavg
etwst
inférieures et unmeetR
plus élevé (la différence peut être encore plus significative avec une charge en arrière-plan).
Remarque : La charge en arrière-plan peut avoir un impact sur le résultat du débit et sur le tuple other_ms
dans le test de latence. Seul le fifo_ms
peut afficher des résultats similaires tant que le chargement en arrière-plan a une priorité inférieure à RT-fifo
.
Spécifier les valeurs de paire
Chaque processus client est associé à un processus serveur dédié au client, et chaque paire peut être planifiée indépendamment sur n'importe quel processeur. Cependant, la migration du processeur ne devrait pas avoir lieu pendant une transaction tant que l'indicateur SYNC est honor
.
Assurez-vous que le système n'est pas surchargé ! Même si une latence élevée est attendue dans un système surchargé, les résultats des tests pour un système surchargé ne fournissent pas d’informations utiles. Pour tester un système avec une pression plus élevée, utilisez -pair #cpu-1
(ou -pair #cpu
avec prudence). Les tests utilisant -pair n
avec n > #cpu
surchargent le système et génèrent des informations inutiles.
Spécifier les valeurs de délai
Après des tests approfondis de scénarios utilisateur (exécution du test de latence sur un produit qualifié), nous avons déterminé que 2,5 ms était le délai à respecter. Pour les nouvelles candidatures ayant des exigences plus élevées (telles que 1 000 photos/seconde), cette valeur limite changera.
Spécifier une sortie détaillée
L’utilisation de l’option -v
affiche une sortie détaillée. Exemple:
libhwbinder_latency -i 1 -v
-------------------------------------------------- service pid: 8674 tid: 8674 cpu: 1 SCHED_OTHER 0-------------------------------------------------- main pid: 8673 tid: 8673 cpu: 1 -------------------------------------------------- client pid: 8677 tid: 8677 cpu: 0 SCHED_OTHER 0-------------------------------------------------- fifo-caller pid: 8677 tid: 8678 cpu: 0 SCHED_FIFO 99 -------------------------------------------------- hwbinder pid: 8674 tid: 8676 cpu: 0 ??? 99-------------------------------------------------- other-caller pid: 8677 tid: 8677 cpu: 0 SCHED_OTHER 0 -------------------------------------------------- hwbinder pid: 8674 tid: 8676 cpu: 0 SCHED_OTHER 0
- Le thread de service est créé avec une priorité
SCHED_OTHER
et exécuté dansCPU:1
avecpid 8674
. - La première transaction est ensuite lancée par un
fifo-caller
. Pour traiter cette transaction, le hwbinder met à niveau la priorité du serveur (pid: 8674 tid: 8676
) à 99 et le marque également avec une classe de planification transitoire (imprimée sous la forme???
). Le planificateur place ensuite le processus serveur dansCPU:0
pour l'exécuter et le synchronise avec le même processeur que son client. - Le deuxième appelant de la transaction a une priorité
SCHED_OTHER
. Le serveur se rétrograde et dessert l'appelant avec la prioritéSCHED_OTHER
.
Utiliser la trace pour le débogage
Vous pouvez spécifier l'option -trace
pour déboguer les problèmes de latence. Lorsqu'il est utilisé, le test de latence arrête l'enregistrement du tracelog au moment où une mauvaise latence est détectée. Exemple:
atrace --async_start -b 8000 -c sched idle workq binder_driver sync freq
libhwbinder_latency -deadline_us 50000 -trace -i 50000 -pair 3
deadline triggered: halt ∓ stop trace log:/sys/kernel/debug/tracing/trace
Les composants suivants peuvent avoir un impact sur la latence :
- Mode de construction Android . Le mode Eng est généralement plus lent que le mode userdebug.
- Cadre . Comment le service framework utilise
ioctl
pour configurer le classeur ? - Pilote de classeur . Le pilote prend-il en charge le verrouillage à granularité fine ? Contient-il tous les correctifs d'amélioration des performances ?
- Version noyau . Plus le noyau dispose de capacités en temps réel, meilleurs sont les résultats.
- Configuration du noyau . La configuration du noyau contient-elle des configurations
DEBUG
telles queDEBUG_PREEMPT
etDEBUG_SPIN_LOCK
? - Planificateur de noyau . Le noyau dispose-t-il d'un planificateur Energy-Aware (EAS) ou d'un planificateur Heterogeneous Multi-Processing (HMP) ? Les pilotes du noyau (pilote
cpu-freq
, pilotecpu-idle
,cpu-hotplug
, etc.) ont-ils un impact sur le planificateur ?