Tests de performances

Android 8.0 inclut des tests de performance des liaisons et des hwbinders pour le débit et la latence. De nombreux scénarios permettent de détecter les performances perceptibles exécuter de tels scénarios peut prendre beaucoup de temps et les résultats sont souvent est indisponible avant l'intégration d'un système. Utiliser les performances fournies facilitent les tests pendant le développement, la détection de problèmes graves plus tôt et améliorer l'expérience utilisateur.

Les tests de performance comprennent les quatre catégories suivantes:

  • débit du binder (disponible dans system/libhwbinder/vts/performance/Benchmark_binder.cpp).
  • latence de liaison (disponible dans frameworks/native/libs/binder/tests/schd-dbg.cpp).
  • le débit du nœud de calcul (disponible dans system/libhwbinder/vts/performance/Benchmark.cpp).
  • Latence de hwbinder (disponible dans system/libhwbinder/vts/performance/Latency.cpp).

À propos de binder et de hwbinder

Le classeur et le hwbinder sont la communication inter-processus (IPC) Android d'infrastructures qui partagent le même pilote Linux, mais avec les éléments suivants : différences qualitatives:

Aspect classeur hwbinder
Objectif Fournir un schéma d'IPC à usage général pour le framework Communiquer avec le matériel
Propriété Optimisé pour l'utilisation du framework Android Frais généraux minimaux et faible latence
Modifier la règle de planification pour le premier plan/l'arrière-plan Oui Non
Transmission d'arguments Utilise la sérialisation compatible avec l'objet Parcel Utilise des tampons de dispersion et évite la surcharge de la copie des données requises pour Sérialisation des parcelles
Héritage de la priorité Non Oui

Processus de liaison et de hwbinder

Un visualiseur Systrace affiche les transactions comme suit:

Figure 1 : Visualisation du lien par Systrace processus.

Dans l'exemple ci-dessus:

  • Les quatre (4) processus schd-dbg sont des processus client.
  • Les quatre (4) processus de liaison sont des processus de serveur (le nom commence par Liaison et se termine par un numéro de séquence).
  • Un processus client est toujours associé à un processus serveur, qui est dédié à son client.
  • Toutes les paires de processus client-serveur sont planifiées indépendamment par noyau simultanément.

Dans le processeur 1, le noyau du système d'exploitation exécute le client pour émettre la requête. Ensuite, utilise le même processeur chaque fois que possible pour activer un processus de serveur, gérer requête, puis revenez au contexte une fois la requête terminée.

Débit et latence

Dans une transaction parfaite, où les processus client et serveur changent les tests de débit et de latence ne produisent pas de résultats messages. Toutefois, lorsque le noyau du système d'exploitation traite une requête d'interruption, qu'il s'agisse du matériel, de l'attente de verrous ou simplement du choix de ne pas gérer un message immédiatement, une bulle de latence peut se former.

Figure 2 : Info-bulle de latence en raison des différences de le débit et la latence.

Le test de débit génère un grand nombre de transactions avec différentes les tailles de charge utile, ce qui fournit une bonne estimation de la durée normale de la transaction (dans (meilleurs scénarios) et le débit maximal que la liaison peut atteindre.

En revanche, le test de latence n'effectue aucune action sur la charge utile pour minimiser à l'heure normale de la transaction. Nous pouvons utiliser le temps de transaction pour estimer la liaison les frais généraux, faire des statistiques pour le pire des cas et calculer le ratio les 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é supérieure est logiquement en attente d'un thread avec une priorité inférieure. Les applications en temps réel ont un problème d'inversion de priorité:

Figure 3 : Inversion des priorités en temps réel applications.

Lors de l'utilisation de la planification CFS (Completely Fair Scheduler) de Linux, un thread s'exécute toujours peut s'exécuter même lorsque d'autres threads ont une priorité plus élevée. Par conséquent, les applications avec planification CFS gèrent l'inversion de priorité comme le comportement attendu et non comme un problème. Dans les cas où le framework Android a besoin d'une planification en temps réel pour garantir le privilège des threads à priorité élevée, doit être résolu.

Exemple d'inversion de priorité lors d'une transaction de liaison (le thread RT est bloquée logiquement par d'autres threads SCP en attendant qu'un thread de liaison service):

Figure 4 : Inversion des priorités, blocage en temps réel fils de discussion.

Pour éviter les blocages, vous pouvez utiliser l'héritage des priorités afin d'escalader temporairement le problème du thread de liaison à un thread RT lorsqu'il traite une requête d'un client RT. N'oubliez pas que la planification en temps réel dispose de ressources limitées et doit être utilisée avec précaution. Dans un système avec n processeurs, le nombre maximal de processeurs en temps réel La valeur "threads" correspond également à n. d'autres threads en temps réel peuvent devoir patienter ne respectent pas 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 pour le binder et le hwbinder. Cependant, comme le binder est largement utilisé sur l'ensemble du système, l'activation de l'héritage de priorité pour les transactions de liaison peut de spammer le système avec plus de threads en temps réel 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 de transaction de liaison/hwbinder. Dans un système non surchargé, les bulles de latence sont rares et leur impact peuvent être éliminées tant que le nombre d'itérations est suffisamment élevé.

  • Le test de débit de binder est en cours system/libhwbinder/vts/performance/Benchmark_binder.cpp
  • Le test de débit hwbinder est en cours system/libhwbinder/vts/performance/Benchmark.cpp

Résultats des tests

Exemple de résultats des tests de débit pour des transactions utilisant différentes charges utiles tailles:

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
  • Time indique le délai aller-retour mesuré en temps réel.
  • Processeur indique le temps cumulé lors de la planification des processeurs pour le test.
  • Les itérations indiquent le nombre de fois où la fonction de test exécuté.

Par exemple, pour une charge utile de 8 octets:

BM_sendVec_binderize/8         69974 ns      32700 ns      21296

... le débit maximal que la liaison peut atteindre est calculé comme suit:

Débit MAX avec une charge utile de 8 octets = (8 * 21296)/69974 ~= 2,423 b/ns ~= 2,268 Go/s

Options de test

Pour obtenir les résultats au format .json, exécutez le test avec la commande 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 qu'il faut au client pour commencer initialiser la transaction, passer au processus serveur pour la gestion, pour recevoir le résultat. Le test recherche également les comportements de programmeur connus qui peut avoir un impact négatif sur la latence des transactions, par exemple avec un planificateur qui ne prendre en charge l'héritage des priorités ou respecter l'indicateur de synchronisation.

  • Le test de latence de la liaison frameworks/native/libs/binder/tests/schd-dbg.cpp
  • Le test de latence du mécanisme de liaison est en cours system/libhwbinder/vts/performance/Latency.cpp

Résultats des tests

Les résultats (au format .json) présentent des statistiques pour la latence moyenne, la meilleure/la pire et la le nombre d'échéances manquées.

Options de test

Les tests de latence utilisent 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 Indiquez la date limite en nous.
-v Permet d'obtenir une sortie détaillée (débogage).
-trace Interrompre la trace une fois le délai atteint.

Les sections suivantes détaillent chaque option, décrivent l'utilisation et fournissent des exemples de résultats.

Spécifier des itérations

Exemple avec un grand nombre d'itérations et la 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 test indiquent les éléments suivants:

"pair":3
Crée une paire client-serveur.
"iterations": 5000
Inclut 5 000 itérations.
"deadline_us":2500
Le délai est 2 500us (2,5 ms). la plupart des transactions sont susceptibles de respecter ce .
"I": 10000
Une seule itération de test comprend deux (2) transactions: <ph type="x-smartling-placeholder">
    </ph>
  • Une transaction par priorité normale (CFS other)
  • Une transaction par priorité en temps réel (RT-fifo)
5 000 itérations correspondent à un total de 10 000 transactions.
"S": 9352
9 352 transactions sont synchronisées sur le même processeur.
"R": 0.9352
Indique le ratio de synchronisation entre le client et le serveur avec le même processeur.
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996}
La moyenne (avg), la pire (wst) et la meilleure (bst) pour toutes les transactions émises par un appelant de priorité normale. Deux transactions miss la date limite, ce qui donne le ratio de conformité (meetR) 0,9996
"fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
Semblable à other_ms, mais pour les transactions émises par un client avec Priorité rt_fifo. Il est probable (mais pas obligatoire) que fifo_ms a un meilleur résultat que other_ms, avec des Valeurs avg et wst, et meetR supérieure (la différence peut être encore plus importante avec la charge en arrière-plan).

Remarque:La charge en arrière-plan peut affecter le débit et le tuple other_ms dans le test de latence. Seuls les fifo_ms peut afficher des résultats similaires tant que le chargement en arrière-plan avec une priorité inférieure à RT-fifo.

Spécifier des valeurs de paire

Chaque processus client est associé à un processus serveur dédié au client, et chaque paire peut être programmée indépendamment de n'importe quel CPU. Toutefois, le processeur la migration ne doit pas avoir lieu pendant une transaction tant que l'indicateur SYNC est honor

Vérifiez que le système n'est pas surchargé. Alors qu'une latence élevée dans un environnement les résultats des tests pour un système surchargé ne sont pas utiles des informations. Pour tester un système avec une pression plus élevée, utilisez -pair #cpu-1 (ou -pair #cpu avec précaution). Tester avec -pair n avec n > #cpu surcharge et génère 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 produit qualifié), nous avons déterminé que l'échéance à respecter était de 2,5 ms. Neuf pour les applications exigeant des exigences plus élevées (par exemple, 1 000 photos/seconde), la valeur du délai sera modifiée.

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 un SCHED_OTHER et l'exécuter dans CPU:1 avec pid 8674.
  • La première transaction est ensuite lancée par fifo-caller Pour assurer le traitement de cette transaction, le mécanisme d'association met à niveau la priorité du serveur (pid: 8674 tid: 8676) sur 99 et l'indique également avec une classe de planification temporaire (imprimée sous la forme ???). Le programmeur puis met le processus serveur dans CPU:0 pour s'exécuter et le synchronise avec même CPU avec son client.
  • L'appelant de la deuxième transaction a une Priorité SCHED_OTHER. Le serveur revient à une version antérieure appelant avec une 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. Quand ? utilisé, le test de latence arrête l'enregistrement du tracelog au moment où 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 compilation Android Le mode Eng est généralement plus lent que mode débogage utilisateur.
  • Framework : Comment le service de framework utilise-t-il ioctl pour configurer la liaison ?
  • Pilote de liaison. Le pilote accepte-t-il les contrôles le verrouillage ? Contient-il tous les correctifs de performance ?
  • Version du noyau. Plus la capacité en temps réel du noyau est meilleurs sont les résultats.
  • Configuration du noyau. La configuration du noyau contient-elle Les configurations DEBUG telles que DEBUG_PREEMPT et DEBUG_SPIN_LOCK?
  • Programmeur de noyau. Le noyau dispose-t-il d’un programmeur (EAS) ou HMP (Hétérogeneous Multi-Processing) ? N'importe quel noyau chauffeurs (cpu-freq chauffeur, cpu-idle conducteur, cpu-hotplug, etc.) sur le planificateur ?