Android 8.0 include test delle prestazioni di binder e hwbinder per velocità effettiva e latenza. Sebbene esistano molti scenari per rilevare problemi prestazionali percepibili, l'esecuzione di tali scenari può richiedere molto tempo e i risultati spesso non sono disponibili fino a quando il sistema non viene integrato. L'utilizzo dei test delle prestazioni forniti semplifica il test durante lo sviluppo, rileva tempestivamente problemi gravi e migliora l'esperienza dell'utente.
I test delle prestazioni includono le seguenti quattro categorie:
- throughput del raccoglitore (disponibile in
system/libhwbinder/vts/performance/Benchmark_binder.cpp
) - latenza del raccoglitore (disponibile in
frameworks/native/libs/binder/tests/schd-dbg.cpp
) - throughput di hwbinder (disponibile in
system/libhwbinder/vts/performance/Benchmark.cpp
) - latenza hwbinder (disponibile in
system/libhwbinder/vts/performance/Latency.cpp
)
Informazioni su raccoglitore e hwbinder
Binder e hwbinder sono infrastrutture di comunicazione interprocesso (IPC) Android che condividono lo stesso driver Linux ma presentano le seguenti differenze qualitative:
Aspetto | raccoglitore | hwbinder |
---|---|---|
Scopo | Fornire uno schema IPC di scopo generale per il framework | Comunicare con l'hardware |
Proprietà | Ottimizzato per l'utilizzo del framework Android | Latenza minima ambientale bassa |
Modifica la politica di pianificazione per primo piano/sfondo | SÌ | NO |
Argomenti che passano | Utilizza la serializzazione supportata dall'oggetto Parcel | Utilizza buffer di dispersione ed evita il sovraccarico per copiare i dati richiesti per la serializzazione di Parcel |
Eredità prioritaria | NO | SÌ |
Processi Binder e hwbinder
Un visualizzatore systrace visualizza le transazioni come segue:
Nell'esempio sopra:
- I quattro (4) processi schd-dbg sono processi client.
- I quattro (4) processi del raccoglitore sono processi server (il nome inizia con Binder e termina con un numero di sequenza).
- Un processo client è sempre accoppiato con un processo server, dedicato al suo client.
- Tutte le coppie di processi client-server vengono pianificate in modo indipendente dal kernel contemporaneamente.
Nella CPU 1, il kernel del sistema operativo esegue il client per emettere la richiesta. Quindi utilizza la stessa CPU quando possibile per riattivare un processo del server, gestire la richiesta e tornare indietro al contesto una volta completata la richiesta.
Throughput e latenza
In una transazione perfetta, in cui i processi client e server cambiano senza soluzione di continuità, i test di throughput e latenza non producono messaggi sostanzialmente diversi. Tuttavia, quando il kernel del sistema operativo gestisce una richiesta di interruzione (IRQ) dall'hardware, attende blocchi o semplicemente sceglie di non gestire un messaggio immediatamente, può formarsi una bolla di latenza.
Il test di throughput genera un gran numero di transazioni con diverse dimensioni di carico utile, fornendo una buona stima del tempo di transazione regolare (negli scenari migliori) e del throughput massimo che il raccoglitore può raggiungere.
Al contrario, il test di latenza non esegue alcuna azione sul carico utile per ridurre al minimo il tempo di transazione regolare. Possiamo utilizzare il tempo di transazione per stimare il sovraccarico del raccoglitore, creare statistiche per il caso peggiore e calcolare il rapporto tra transazioni la cui latenza soddisfa una scadenza specifica.
Gestire le inversioni di priorità
Un'inversione di priorità si verifica quando un thread con priorità più alta attende logicamente un thread con priorità più bassa. Le applicazioni in tempo reale (RT) presentano un problema di inversione di priorità:
Quando si utilizza la pianificazione Linux Completely Fair Scheduler (CFS), un thread ha sempre la possibilità di essere eseguito anche quando altri thread hanno una priorità più alta. Di conseguenza, le applicazioni con pianificazione CFS gestiscono l'inversione di priorità come comportamento previsto e non come un problema. Tuttavia, nei casi in cui il framework Android necessita della pianificazione RT per garantire il privilegio dei thread ad alta priorità, è necessario risolvere l'inversione di priorità.
Esempio di inversione di priorità durante una transazione del raccoglitore (il thread RT viene logicamente bloccato da altri thread CFS durante l'attesa del servizio di un thread del raccoglitore):
Per evitare blocchi, è possibile utilizzare l'ereditarietà della priorità per escalare temporaneamente il thread Binder a un thread RT quando serve una richiesta da un client RT. Tieni presente che la pianificazione RT ha risorse limitate e deve essere utilizzata con attenzione. In un sistema con n CPU, anche il numero massimo di thread RT correnti è n ; thread RT aggiuntivi potrebbero dover attendere (e quindi non rispettare le scadenze) se tutte le CPU vengono occupate da altri thread RT.
Per risolvere tutte le possibili inversioni di priorità, è possibile utilizzare l'ereditarietà della priorità sia per binder che per hwbinder. Tuttavia, poiché il raccoglitore è ampiamente utilizzato nel sistema, abilitare l'ereditarietà della priorità per le transazioni del raccoglitore potrebbe inviare spam al sistema con più thread RT di quanti ne possa gestire.
Esegui test di throughput
Il test della velocità effettiva viene eseguito rispetto alla velocità effettiva delle transazioni binder/hwbinder. In un sistema non sovraccaricato, le bolle di latenza sono rare e il loro impatto può essere eliminato purché il numero di iterazioni sia sufficientemente elevato.
- Il test di throughput del raccoglitore si trova in
system/libhwbinder/vts/performance/Benchmark_binder.cpp
. - Il test del throughput di hwbinder si trova in
system/libhwbinder/vts/performance/Benchmark.cpp
.
Risultati del test
Risultati di esempio del test di throughput per transazioni che utilizzano dimensioni di payload diverse:
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
- Il tempo indica il ritardo di andata e ritorno misurato in tempo reale.
- CPU indica il tempo accumulato in cui le CPU sono pianificate per il test.
- Iterazioni indica il numero di volte in cui è stata eseguita la funzione di test.
Ad esempio, per un payload di 8 byte:
BM_sendVec_binderize/8 69974 ns 32700 ns 21296
… la produttività massima che il rilegatore può ottenere viene calcolata come:
Throughput MAX con payload di 8 byte = (8 * 21296)/69974 ~= 2,423 b/ns ~= 2,268 Gb/s
Opzioni di prova
Per ottenere risultati in .json, esegui il test con l'argomento --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"
},
….
}
Esegui test di latenza
Il test di latenza misura il tempo impiegato dal client per iniziare a inizializzare la transazione, passare al processo del server per la gestione e ricevere il risultato. Il test cerca anche comportamenti errati noti dello scheduler che possono avere un impatto negativo sulla latenza delle transazioni, come uno scheduler che non supporta l'ereditarietà della priorità o non rispetta il flag di sincronizzazione.
- Il test di latenza del raccoglitore si trova in
frameworks/native/libs/binder/tests/schd-dbg.cpp
. - Il test di latenza di hwbinder si trova in
system/libhwbinder/vts/performance/Latency.cpp
.
Risultati del test
I risultati (in formato .json) mostrano le statistiche per la latenza media/migliore/peggiore e il numero di scadenze mancate.
Opzioni di prova
I test di latenza accettano le seguenti opzioni:
Comando | Descrizione |
---|---|
-i value | Specificare il numero di iterazioni. |
-pair value | Specificare il numero di coppie di processi. |
-deadline_us 2500 | Specificare la scadenza in noi. |
-v | Ottieni output dettagliato (debug). |
-trace | Interrompe la traccia in caso di scadenza raggiunta. |
Le sezioni seguenti descrivono dettagliatamente ciascuna opzione, descrivono l'utilizzo e forniscono risultati di esempio.
Specificare le iterazioni
Esempio con un numero elevato di iterazioni e output dettagliato disabilitato:
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"
}
I risultati dei test mostrano quanto segue:
-
"pair":3
- Crea una coppia client e server.
-
"iterations": 5000
- Include 5000 iterazioni.
-
"deadline_us":2500
- La scadenza è 2500us (2,5 ms); si prevede che la maggior parte delle transazioni raggiunga questo valore.
-
"I": 10000
- Una singola iterazione del test include due (2) transazioni:
- Una transazione con priorità normale (
CFS other
) - Una transazione con priorità in tempo reale (
RT-fifo
)
- Una transazione con priorità normale (
-
"S": 9352
- 9352 transazioni sono sincronizzate nella stessa CPU.
-
"R": 0.9352
- Indica il rapporto con cui il client e il server sono sincronizzati insieme nella stessa CPU.
-
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996}
- Il caso medio (
avg
), peggiore (wst
) e migliore (bst
) per tutte le transazioni emesse da un normale chiamante prioritario. Due transazionimiss
la scadenza, rendendo il rapporto di incontro (meetR
) 0,9996. -
"fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
- Simile a
other_ms
, ma per transazioni emesse dal client con prioritàrt_fifo
. È probabile (ma non obbligatorio) chefifo_ms
abbia un risultato migliore rispetto aother_ms
, con valoriavg
ewst
inferiori e unmeetR
più alto (la differenza può essere ancora più significativa con il carico in background).
Nota: il carico in background può influire sul risultato della velocità effettiva e sulla tupla other_ms
nel test di latenza. Solo fifo_ms
può mostrare risultati simili purché il caricamento in background abbia una priorità inferiore rispetto a RT-fifo
.
Specificare i valori della coppia
Ogni processo client è accoppiato con un processo server dedicato per il client e ogni coppia può essere pianificata in modo indipendente su qualsiasi CPU. Tuttavia, la migrazione della CPU non dovrebbe avvenire durante una transazione finché il flag SYNC è honor
.
Assicurarsi che il sistema non sia sovraccaricato! Sebbene sia prevista un'elevata latenza in un sistema sovraccarico, i risultati dei test per un sistema sovraccarico non forniscono informazioni utili. Per testare un sistema con pressione più elevata, utilizzare -pair #cpu-1
(o -pair #cpu
con cautela). Testare utilizzando -pair n
con n > #cpu
sovraccarica il sistema e genera informazioni inutili.
Specificare i valori di scadenza
Dopo test approfonditi sullo scenario utente (eseguendo il test di latenza su un prodotto qualificato), abbiamo stabilito che 2,5 ms è la scadenza da rispettare. Per le nuove applicazioni con requisiti più elevati (come 1000 foto al secondo), questo valore di scadenza cambierà.
Specificare l'output dettagliato
Utilizzando l'opzione -v
viene visualizzato un output dettagliato. Esempio:
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
- Il thread del servizio viene creato con una priorità
SCHED_OTHER
ed eseguito inCPU:1
conpid 8674
. - La prima transazione viene quindi avviata da un
fifo-caller
. Per servire questa transazione, hwbinder aggiorna la priorità del server (pid: 8674 tid: 8676
) a 99 e la contrassegna anche con una classe di pianificazione transitoria (stampata come???
). Lo scheduler inserisce quindi il processo del server inCPU:0
per l'esecuzione e lo sincronizza con la stessa CPU con il suo client. - Il secondo chiamante della transazione ha una priorità
SCHED_OTHER
. Il server esegue il downgrade e fornisce servizi al chiamante con prioritàSCHED_OTHER
.
Utilizza la traccia per il debug
È possibile specificare l'opzione -trace
per eseguire il debug dei problemi di latenza. Quando utilizzato, il test di latenza interrompe la registrazione del tracelog nel momento in cui viene rilevata una latenza eccessiva. Esempio:
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
I seguenti componenti possono influire sulla latenza:
- Modalità di creazione Android . La modalità Eng è solitamente più lenta della modalità userdebug.
- Struttura . In che modo il servizio framework utilizza
ioctl
per la configurazione del raccoglitore? - Driver del raccoglitore . Il driver supporta il blocco a grana fine? Contiene tutte le patch che migliorano le prestazioni?
- Versione del kernel . Migliore è la capacità in tempo reale del kernel, migliori saranno i risultati.
- Configurazione del kernel . La configurazione del kernel contiene configurazioni
DEBUG
comeDEBUG_PREEMPT
eDEBUG_SPIN_LOCK
? - Programmatore del kernel . Il kernel dispone di uno scheduler Energy-Aware (EAS) o di uno scheduler Heterogeneous Multi-Processing (HMP)? Eventuali driver del kernel ( driver
cpu-freq
, drivercpu-idle
,cpu-hotplug
, ecc.) influiscono sullo scheduler?