Android 8.0 umfasst Binder- und Hwbinder-Leistungstests für Durchsatz und Latenz. Zwar gibt es viele Szenarien zur Erkennung wahrnehmbarer Leistungsprobleme, die Ausführung solcher Szenarien kann jedoch zeitaufwändig sein und die Ergebnisse stehen oft erst nach der Integration eines Systems zur Verfügung. Durch die Verwendung der bereitgestellten Leistungstests können Tests während der Entwicklung einfacher durchgeführt, schwerwiegende Probleme früher erkannt und die Benutzererfahrung verbessert werden.
Leistungstests umfassen die folgenden vier Kategorien:
- Bindemitteldurchsatz (verfügbar in
system/libhwbinder/vts/performance/Benchmark_binder.cpp
) - Binder-Latenz (verfügbar in
frameworks/native/libs/binder/tests/schd-dbg.cpp
“) - hwbinder-Durchsatz (verfügbar in
system/libhwbinder/vts/performance/Benchmark.cpp
) - hwbinder-Latenz (verfügbar in
system/libhwbinder/vts/performance/Latency.cpp
)
Über Binder und Hwbinder
Binder und hwbinder sind IPC-Infrastrukturen (Android Inter-Process Communication), die denselben Linux-Treiber verwenden, jedoch die folgenden qualitativen Unterschiede aufweisen:
Aspekt | Bindemittel | hwbinder |
---|---|---|
Zweck | Stellen Sie ein allgemeines IPC-Schema für das Framework bereit | Kommunizieren Sie mit Hardware |
Eigentum | Optimiert für die Verwendung des Android-Frameworks | Minimaler Overhead, geringe Latenz |
Planungsrichtlinie für Vordergrund/Hintergrund ändern | Ja | NEIN |
Argumente vorbei | Verwendet die vom Parcel-Objekt unterstützte Serialisierung | Verwendet Streupuffer und vermeidet den Mehraufwand für das Kopieren von Daten, die für die Parcel-Serialisierung erforderlich sind |
Prioritätsvererbung | NEIN | Ja |
Binder- und Hwbinder-Prozesse
Ein Systrace-Visualizer zeigt Transaktionen wie folgt an:
Im obigen Beispiel:
- Die vier (4) schd-dbg-Prozesse sind Client-Prozesse.
- Die vier (4) Binder-Prozesse sind Serverprozesse (Name beginnt mit Binder und endet mit einer Sequenznummer).
- Ein Clientprozess ist immer mit einem Serverprozess gekoppelt, der seinem Client gewidmet ist.
- Alle Client-Server-Prozesspaare werden unabhängig vom Kernel gleichzeitig geplant.
In CPU 1 führt der Betriebssystemkernel den Client aus, um die Anforderung auszugeben. Anschließend wird nach Möglichkeit dieselbe CPU verwendet, um einen Serverprozess zu aktivieren, die Anfrage zu bearbeiten und nach Abschluss der Anfrage wieder in den Kontext zu wechseln.
Durchsatz vs. Latenz
Bei einer perfekten Transaktion, bei der der Client- und der Serverprozess nahtlos wechseln, führen Durchsatz- und Latenztests nicht zu wesentlich unterschiedlichen Nachrichten. Wenn der Betriebssystemkern jedoch eine Interrupt-Anfrage (IRQ) von der Hardware verarbeitet, auf Sperren wartet oder sich einfach dafür entscheidet, eine Nachricht nicht sofort zu verarbeiten, kann sich eine Latenzblase bilden.
Der Durchsatztest generiert eine große Anzahl von Transaktionen mit unterschiedlichen Nutzlastgrößen und liefert eine gute Schätzung der regulären Transaktionszeit (im besten Fall) und des maximalen Durchsatzes, den der Binder erreichen kann.
Im Gegensatz dazu führt der Latenztest keine Aktionen an der Nutzlast durch, um die reguläre Transaktionszeit zu minimieren. Wir können die Transaktionszeit verwenden, um den Binder-Overhead abzuschätzen, Statistiken für den schlimmsten Fall zu erstellen und das Verhältnis der Transaktionen zu berechnen, deren Latenz eine bestimmte Frist einhält.
Behandeln Sie Prioritätsumkehrungen
Eine Prioritätsumkehr tritt auf, wenn ein Thread mit höherer Priorität logisch auf einen Thread mit niedrigerer Priorität wartet. Bei Echtzeitanwendungen (RT) besteht ein Problem der Prioritätsumkehr:
Bei Verwendung der Linux Completely Fair Scheduler (CFS)-Planung hat ein Thread immer die Chance, ausgeführt zu werden, auch wenn andere Threads eine höhere Priorität haben. Daher behandeln Anwendungen mit CFS-Planung die Prioritätsumkehr als erwartetes Verhalten und nicht als Problem. In Fällen, in denen das Android-Framework RT-Planung benötigt, um die Privilegien von Threads mit hoher Priorität zu gewährleisten, muss die Prioritätsumkehr jedoch gelöst werden.
Beispiel für eine Prioritätsumkehr während einer Binder-Transaktion (RT-Thread wird logisch von anderen CFS-Threads blockiert, wenn er auf die Bedienung eines Binder-Threads wartet):
Um Blockaden zu vermeiden, können Sie die Prioritätsvererbung verwenden, um den Binder-Thread vorübergehend zu einem RT-Thread zu eskalieren, wenn er eine Anfrage von einem RT-Client bearbeitet. Bedenken Sie, dass die RT-Planung über begrenzte Ressourcen verfügt und sorgfältig eingesetzt werden sollte. In einem System mit n CPUs beträgt die maximale Anzahl aktueller RT-Threads ebenfalls n ; Zusätzliche RT-Threads müssen möglicherweise warten (und verpassen somit ihre Fristen), wenn alle CPUs von anderen RT-Threads belegt sind.
Um alle möglichen Prioritätsumkehrungen aufzulösen, können Sie die Prioritätsvererbung sowohl für binder als auch für hwbinder verwenden. Da Binder jedoch im gesamten System weit verbreitet ist, könnte die Aktivierung der Prioritätsvererbung für Binder-Transaktionen das System mit mehr RT-Threads überfluten, als es bedienen kann.
Führen Sie Durchsatztests durch
Der Durchsatztest wird anhand des Transaktionsdurchsatzes von Binder/HWBinder durchgeführt. In einem nicht überlasteten System sind Latenzblasen selten und ihre Auswirkungen können beseitigt werden, solange die Anzahl der Iterationen hoch genug ist.
- Der Binder- Durchsatztest befindet sich in
system/libhwbinder/vts/performance/Benchmark_binder.cpp
. - Der hwbinder- Durchsatztest befindet sich in
system/libhwbinder/vts/performance/Benchmark.cpp
.
Testergebnisse
Beispielhafte Durchsatztestergebnisse für Transaktionen mit unterschiedlichen Nutzlastgrößen:
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
- Die Zeit gibt die in Echtzeit gemessene Umlaufverzögerung an.
- CPU gibt die Gesamtzeit an, in der CPUs für den Test eingeplant sind.
- Iterationen geben an, wie oft die Testfunktion ausgeführt wurde.
Beispiel für eine 8-Byte-Nutzlast:
BM_sendVec_binderize/8 69974 ns 32700 ns 21296
… der maximale Durchsatz, den der Binder erreichen kann, errechnet sich zu:
MAX. Durchsatz mit 8-Byte-Nutzlast = (8 * 21296)/69974 ~= 2,423 b/ns ~= 2,268 Gbit/s
Testmöglichkeiten
Um Ergebnisse in .json zu erhalten, führen Sie den Test mit dem Argument --benchmark_format=json
aus:
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"
},
….
}
Führen Sie Latenztests durch
Der Latenztest misst die Zeit, die der Client benötigt, um mit der Initialisierung der Transaktion zu beginnen, zum Serverprozess zur Verarbeitung zu wechseln und das Ergebnis zu erhalten. Der Test sucht auch nach bekanntem fehlerhaftem Scheduler-Verhalten, das sich negativ auf die Transaktionslatenz auswirken kann, z. B. einem Scheduler, der die Prioritätsvererbung nicht unterstützt oder das Synchronisierungsflag nicht berücksichtigt.
- Der Binder-Latenztest befindet sich in
frameworks/native/libs/binder/tests/schd-dbg.cpp
. - Der Hwbinder-Latenztest befindet sich in
system/libhwbinder/vts/performance/Latency.cpp
.
Testergebnisse
Die Ergebnisse (in .json) zeigen Statistiken zur durchschnittlichen/besten/schlechtesten Latenz und der Anzahl der verpassten Fristen.
Testmöglichkeiten
Latenztests bieten die folgenden Optionen:
Befehl | Beschreibung |
---|---|
-i value | Geben Sie die Anzahl der Iterationen an. |
-pair value | Geben Sie die Anzahl der Prozesspaare an. |
-deadline_us 2500 | Geben Sie uns die Frist an. |
-v | Erhalten Sie eine ausführliche (Debug-)Ausgabe. |
-trace | Stoppen Sie die Verfolgung bei Fristüberschreitung. |
In den folgenden Abschnitten werden die einzelnen Optionen detailliert beschrieben, die Verwendung beschrieben und Beispielergebnisse bereitgestellt.
Geben Sie Iterationen an
Beispiel mit einer großen Anzahl von Iterationen und deaktivierter ausführlicher Ausgabe:
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"
}
Diese Testergebnisse zeigen Folgendes:
-
"pair":3
- Erstellt ein Client- und Serverpaar.
-
"iterations": 5000
- Enthält 5000 Iterationen.
-
"deadline_us":2500
- Die Frist beträgt 2500 us (2,5 ms); Es wird erwartet, dass die meisten Transaktionen diesen Wert erreichen.
-
"I": 10000
- Eine einzelne Testiteration umfasst zwei (2) Transaktionen:
- Eine Transaktion nach normaler Priorität (
CFS other
) - Eine Transaktion nach Echtzeitpriorität (
RT-fifo
)
- Eine Transaktion nach normaler Priorität (
-
"S": 9352
- 9352 der Transaktionen werden in derselben CPU synchronisiert.
-
"R": 0.9352
- Gibt das Verhältnis an, in dem Client und Server in derselben CPU miteinander synchronisiert werden.
-
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996}
- Der durchschnittliche (
avg
), schlechteste (wst
) und beste (bst
) Fall für alle von einem Anrufer mit normaler Priorität ausgegebenen Transaktionen. Zwei Transaktionenmiss
die Frist, sodass die Erfüllungsquote (meetR
) 0,9996 beträgt. -
"fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
- Ähnlich wie
other_ms
, jedoch für vom Client ausgegebene Transaktionen mitrt_fifo
-Priorität. Es ist wahrscheinlich (aber nicht erforderlich), dassfifo_ms
ein besseres Ergebnis alsother_ms
liefert, mit niedrigerenavg
undwst
Werten und einem höherenmeetR
(der Unterschied kann bei Last im Hintergrund sogar noch größer sein).
Hinweis: Die Hintergrundlast kann sich auf das Durchsatzergebnis und das other_ms
Tupel im Latenztest auswirken. Nur fifo_ms
zeigt möglicherweise ähnliche Ergebnisse, solange die Hintergrundlast eine niedrigere Priorität als RT-fifo
hat.
Geben Sie Paarwerte an
Jeder Client-Prozess ist mit einem für den Client dedizierten Server-Prozess gepaart, und jedes Paar kann unabhängig für jede CPU geplant werden. Die CPU-Migration sollte jedoch nicht während einer Transaktion erfolgen, solange das SYNC-Flag honor
ist.
Stellen Sie sicher, dass das System nicht überlastet wird! Während in einem überlasteten System eine hohe Latenz zu erwarten ist, liefern Testergebnisse für ein überlastetes System keine nützlichen Informationen. Um ein System mit höherem Druck zu testen, verwenden Sie -pair #cpu-1
(oder -pair #cpu
mit Vorsicht). Das Testen mit -pair n
mit n > #cpu
überlastet das System und generiert nutzlose Informationen.
Geben Sie Fristwerte an
Nach umfangreichen Benutzerszenariotests (Durchführen des Latenztests an einem qualifizierten Produkt) haben wir festgestellt, dass 2,5 ms die einzuhaltende Frist sind. Bei Neuanträgen mit höheren Anforderungen (z. B. 1000 Fotos/Sekunde) ändert sich dieser Fristwert.
Geben Sie eine ausführliche Ausgabe an
Wenn Sie die Option -v
verwenden, wird eine ausführliche Ausgabe angezeigt. Beispiel:
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
- Der Service-Thread wird mit einer
SCHED_OTHER
Priorität erstellt und inCPU:1
mitpid 8674
ausgeführt. - Die erste Transaktion wird dann von einem
fifo-caller
gestartet. Um diese Transaktion abzuwickeln, erhöht der Hwbinder die Priorität des Servers (pid: 8674 tid: 8676
) auf 99 und markiert ihn außerdem mit einer vorübergehenden Planungsklasse (gedruckt als???
). Der Scheduler versetzt dann den Serverprozess zur Ausführung inCPU:0
und synchronisiert ihn mit derselben CPU wie sein Client. - Der zweite Transaktionsaufrufer hat eine
SCHED_OTHER
Priorität. Der Server stuft sich selbst herunter und bedient den Anrufer mit der PrioritätSCHED_OTHER
.
Verwenden Sie Trace zum Debuggen
Sie können die Option -trace
angeben, um Latenzprobleme zu beheben. Bei Verwendung stoppt der Latenztest die Tracelog-Aufzeichnung in dem Moment, in dem eine schlechte Latenz erkannt wird. Beispiel:
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
Die folgenden Komponenten können die Latenz beeinflussen:
- Android-Build-Modus . Der Eng-Modus ist normalerweise langsamer als der Userdebug-Modus.
- Rahmen . Wie verwendet der Framework-Dienst
ioctl
zur Konfiguration des Ordners? - Binder-Treiber . Unterstützt der Treiber feinkörniges Sperren? Enthält es alle leistungssteigernden Patches?
- Kernelversion . Je besser die Echtzeitfähigkeit des Kernels ist, desto besser sind die Ergebnisse.
- Kernel-Konfiguration . Enthält die Kernel-Konfiguration
DEBUG
Konfigurationen wieDEBUG_PREEMPT
undDEBUG_SPIN_LOCK
? - Kernel-Scheduler . Verfügt der Kernel über einen Energy-Aware Scheduler (EAS) oder Heterogeneous Multi-Processing (HMP) Scheduler? Haben irgendwelche Kernel-Treiber (
cpu-freq
Treiber,cpu-idle
Treiber,cpu-hotplug
usw.) Auswirkungen auf den Scheduler?