Test delle prestazioni

Android 8.0 include test delle prestazioni di binder e hwbinder per la velocità effettiva e una latenza di pochi millisecondi. Sebbene esistano molti scenari per il rilevamento di prestazioni percepibili problemi, l'esecuzione di questi scenari può richiedere molto tempo e i risultati sono spesso non è disponibile prima dell'integrazione di un sistema. Utilizzo del rendimento fornito semplifica i test durante lo sviluppo, rileva problemi gravi e migliorare l'esperienza utente.

I test del rendimento includono le seguenti quattro categorie:

  • binder (disponibile in system/libhwbinder/vts/performance/Benchmark_binder.cpp
  • di latenza di binder (disponibile frameworks/native/libs/binder/tests/schd-dbg.cpp
  • velocità effettiva hwbinder (disponibile in system/libhwbinder/vts/performance/Benchmark.cpp
  • latenza hwbinder (disponibile in system/libhwbinder/vts/performance/Latency.cpp

Informazioni su binder e hwbinder

Binder e hwbinder sono comunicazione tra processi (IPC, Android Inter-Process Communication) che condividono lo stesso driver Linux ma hanno differenze qualitative:

Aspetto raccoglitore Hwbinder
Finalità Fornisci uno schema IPC per uso generico per il framework Comunica con l'hardware
Proprietà Ottimizzato per l'utilizzo del framework Android Bassa latenza di overhead minimo
Modifica il criterio di pianificazione per il primo piano o lo sfondo No
Argomenti superati Usa la serializzazione supportata dall'oggetto Parcel Utilizza i buffer a dispersione ed evita l'overhead per la copia dei dati richiesti Serializzazione del pacco
Eredità della priorità No

Processi Binder e hwbinder

Un visualizzatore di systrace mostra le transazioni come segue:

Figura 1. Visualizzazione Systrace del raccoglitore processi aziendali.

Nell'esempio riportato sopra:

  • I quattro (4) processi schd-dbg sono processi client.
  • I quattro (4) processi del binder sono processi server (il nome inizia con Binder e termina con un numero di sequenza).
  • Un processo client è sempre associato a un processo server, dedicato al proprio client.
  • Tutte le coppie di processi client-server sono pianificate in modo indipendente dal kernel contemporaneamente.

Nella CPU 1, il kernel del sistema operativo esegue il client per emettere la richiesta. Poi utilizza la stessa CPU quando è possibile per riattivare un processo server, gestire per poi passare nuovamente al contesto una volta completata la richiesta.

Velocità effettiva e latenza

In una transazione perfetta, in cui i processi del client e del server scambiano senza interruzioni, i test di velocità effettiva e latenza non producono messaggi. Tuttavia, quando il kernel del sistema operativo gestisce una richiesta di interruzione (IRQ) dall'hardware, attendere le serrature o semplicemente scegliere di non gestire un messaggio immediatamente, si può formare una bolla di latenza.

Figura 2. Fumetto di latenza dovuto a differenze di velocità effettiva e latenza.

Il test della velocità effettiva genera un numero elevato di transazioni con dimensioni del payload, fornendo una buona stima del tempo di transazione regolare (in nei casi migliori) e la velocità effettiva massima che il binder può raggiungere.

Al contrario, il test di latenza non esegue alcuna azione sul payload per ridurre al minimo la normale durata della transazione. Possiamo usare il tempo della transazione per stimare il raccoglitore overhead, calcola le statistiche per il caso peggiore e calcola il rapporto transazioni la cui latenza soddisfa una scadenza specificata.

Gestire le inversioni di priorità

Si verifica un'inversione di priorità quando un thread con priorità più alta è logicamente in attesa di un thread con priorità inferiore. Le applicazioni Real-Time (RT) hanno un problema di inversione di priorità:

Figura 3. Inversione delle priorità in tempo reale applicazioni.

Quando si utilizza la pianificazione Linux Completely Fair Scheduler (CFS), un thread possono essere eseguiti anche quando altri thread hanno una priorità più alta. Di conseguenza, le applicazioni con pianificazione CFS gestiscono l'inversione di priorità come previsto e non come un problema. Nei casi in cui il framework Android richieda una pianificazione RT per garantire il privilegio dei thread ad alta priorità, tuttavia, l'inversione della priorità devono essere risolti.

Esempio di inversione della priorità durante una transazione binder (il thread RT è bloccati logicamente da altri thread CFS durante l'attesa di un thread binder di servizio):

Figura 4. Inversione delle priorità, blocco in tempo reale thread.

Per evitare blocchi, puoi utilizzare l'ereditarietà prioritaria per riassegnare temporaneamente il thread Binder a un thread RT quando gestisce 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, il numero massimo di istanze RT attuali i thread sono anch'essi n; potrebbe essere necessario attendere altri thread RT (e quindi non rispettano le scadenze) se tutte le CPU sono prese da altri thread RT.

Per risolvere tutte le possibili inversioni di priorità, puoi utilizzare la priorità ereditarietà sia per binder che per hwbinder. Tuttavia, poiché il legante è ampiamente utilizzato nel sistema, l'abilitazione dell'ereditarietà prioritaria per le transazioni binder potrebbe inviare spam al sistema con più thread RT di quanti ne possa servire.

Esegui test della velocità effettiva

Il test della velocità effettiva viene eseguito rispetto alla velocità effettiva delle transazioni binder/hwbinder. Nella un sistema non sovraccarico, le bolle di latenza sono rare e il loro impatto può essere eliminato purché il numero di iterazioni sia sufficientemente elevato.

  • Il test della velocità effettiva binder è in corso system/libhwbinder/vts/performance/Benchmark_binder.cpp.
  • Il test della velocità effettiva hwbinder è in system/libhwbinder/vts/performance/Benchmark.cpp.

Risultati del test

Esempi di risultati del test della velocità effettiva per le transazioni che utilizzano payload diversi dimensioni:

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
  • Tempo indica il ritardo di andata e ritorno misurato in tempo reale.
  • CPU indica il tempo accumulato durante la pianificazione delle CPU per il test.
  • Iterazioni indica il numero di volte in cui la funzione di test eseguito.

Ad esempio, per un payload a 8 byte:

BM_sendVec_binderize/8         69974 ns      32700 ns      21296

...la velocità effettiva massima che il binder può raggiungere viene calcolata come segue:

Velocità effettiva MASSIMA con payload a 8 byte = (8 * 21296)/69974 ~= 2,423 b/ns ~= 2,268 Gb/s

Opzioni di test

Per ottenere i risultati in formato .json, esegui il test con --benchmark_format=json argomento:

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 necessario per l'avvio del client inizializzare la transazione, passare al processo server per la gestione e ricevere il risultato. Il test ricerca anche i comportamenti noti dello scheduler che può influire negativamente sulla latenza delle transazioni, ad esempio uno scheduler che non supportare l'ereditarietà della priorità o rispettare il flag di sincronizzazione.

  • Il test di latenza del binder è in corso frameworks/native/libs/binder/tests/schd-dbg.cpp.
  • Il test di latenza hwbinder è in corso system/libhwbinder/vts/performance/Latency.cpp.

Risultati del test

I risultati (in .json) mostrano le statistiche per la latenza media/migliore/peggiore di scadenze non rispettate.

Opzioni di test

I test di latenza prevedono le seguenti opzioni:

Comando Descrizione
-i value Specifica il numero di iterazioni.
-pair value Specifica il numero di coppie di processi.
-deadline_us 2500 Specifica la scadenza.
-v Ottieni un output dettagliato (debug).
-trace Interrompi la traccia in caso di hit da scadenza.

Le seguenti sezioni descrivono nel dettaglio ciascuna opzione, descrivono l'utilizzo e forniscono di risultati di esempio.

Specifica le iterazioni

Esempio con un numero elevato di iterazioni e output dettagliato disattivati:

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 di questi test mostrano quanto segue:

"pair":3
Crea una coppia client-server.
"iterations": 5000
Include 5000 iterazioni.
"deadline_us":2500
La scadenza è 2500us (2,5 ms); la maggior parte delle transazioni dovrebbe soddisfare questo valore.
"I": 10000
Una singola iterazione di test include due (2) transazioni:
  • Una transazione con priorità normale (CFS other)
  • Una transazione con priorità in tempo reale (RT-fifo)
di Gemini Advanced. 5000 iterazioni equivalgono a un totale di 10.000 transazioni.
"S": 9352
9352 transazioni vengono sincronizzate nella stessa CPU.
"R": 0.9352
Indica il rapporto in base al quale il client e il server vengono sincronizzati insieme la stessa CPU.
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996}
Media (avg), peggiore (wst) e migliore (bst) per tutte le transazioni emesse da un chiamante con priorità normale. Due transazioni miss la scadenza, per rientrare nell'indice (meetR) 0,9996.
"fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
Simile a other_ms, ma per le transazioni emesse dal cliente con Priorità rt_fifo. È probabile (ma non obbligatorio) che fifo_ms ha un risultato migliore di other_ms, con meno Valori avg e wst e un valore di meetR più alto (la differenza può essere ancora più significativa con il caricamento in background).

Nota:il carico in background potrebbe influire sulla velocità effettiva. e la tupla other_ms nel test di latenza. Solo il fifo_ms potrebbe mostrare risultati simili a condizione che il caricamento in background sia con una priorità inferiore a RT-fifo.

Specifica i valori di coppia

Ogni processo del client è abbinato a un processo server dedicato al client, e ogni coppia può essere pianificata in modo indipendente per qualsiasi CPU. Tuttavia, la CPU la migrazione non dovrebbe avvenire durante una transazione, purché il flag SYNC sia honor.

Accertati che il sistema non sia sovraccarico. Mentre la latenza elevata in un ambiente sovraccaricato sistema è previsto, i risultati del test per un sistema sovraccarico non forniscono utili informazioni. Per testare un impianto con una pressione più elevata, usa -pair #cpu-1 (o -pair #cpu con cautela). Test con -pair n con n > #cpu sovraccarica sistema e genera informazioni inutili.

Specifica i valori delle scadenze

Dopo un ampio test degli scenari utente (l'esecuzione del test di latenza su una prodotto idoneo), abbiamo stabilito che la scadenza è di 2,5 ms. Per nuovi per applicazioni con requisiti più elevati (ad esempio 1000 foto/secondo), questo il valore della scadenza cambierà.

Specifica un output dettagliato

Se utilizzi 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 di servizio viene creato con un oggetto Priorità SCHED_OTHER ed esecuzione in CPU:1 con pid 8674.
  • La prima transazione viene quindi avviata da un fifo-caller. Per gestire questa transazione, hwbinder esegue l'upgrade del file la priorità del server (pid: 8674 tid: 8676) sia 99 e la contrassegna anche con una classe di pianificazione temporanea (stampata come ???). Scheduler quindi inserisce il processo del server in CPU:0 e lo sincronizza con con il proprio client.
  • Il chiamante della seconda transazione ha un Priorità SCHED_OTHER. Il server esegue il downgrade stesso e gestisce il servizio chiamante con priorità SCHED_OTHER.

Usa traccia per debug

Puoi specificare l'opzione -trace per eseguire il debug dei problemi di latenza. Quando utilizzata, il test di latenza interrompe la registrazione del tracelog nel momento in cui rilevata la latenza. 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à build Android. La modalità Eng è solitamente più lenta di modalità utente di debug.
  • Framework. Come utilizza il servizio framework ioctl per la configurazione nel raccoglitore?
  • Driver raccoglitore. Il driver supporta l'ottimizzazione il blocco? Contiene tutte le patch di aggiornamento delle prestazioni?
  • Versione kernel. Migliore è la capacità in tempo reale del kernel, migliori sono i risultati.
  • Configurazione del kernel. La configurazione del kernel contiene Configurazioni di DEBUG come DEBUG_PREEMPT e DEBUG_SPIN_LOCK?
  • Strumento di pianificazione del kernel. Il kernel ha un sistema di gestione scheduler (EAS) o scheduler eterogeneo multi-processing (HMP)? Esegui qualsiasi kernel conducenti (cpu-freq autista, cpu-idle autista, cpu-hotplug e così via) influiscono sullo scheduler?