Google si impegna a promuovere l'equità razziale per le comunità nere. Vedi come.
Questa pagina è stata tradotta dall'API Cloud Translation.
Switch to English

Test delle prestazioni

Android 8.0 include test delle prestazioni di binder e hwbinder per velocità effettiva e latenza. Sebbene esistano molti scenari per rilevare problemi di prestazioni percepibili, l'esecuzione di tali scenari può richiedere molto tempo e i risultati sono spesso non disponibili fino a dopo l'integrazione di un sistema. L'uso dei test delle prestazioni forniti semplifica i test durante lo sviluppo, il rilevamento tempestivo di gravi problemi e il miglioramento dell'esperienza dell'utente.

I test delle prestazioni comprendono 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 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 infrastrutture di comunicazione tra processi di Android (IPC) che condividono lo stesso driver Linux ma presentano le seguenti differenze qualitative:

Aspetto raccoglitore hwbinder
Scopo Fornire uno schema IPC generico per il framework Comunicare con l'hardware
Proprietà Ottimizzato per l'utilizzo del framework Android Bassa latenza ambientale minima
Modifica la politica di pianificazione per primo piano / sfondo 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 dei pacchi
Eredità prioritaria No

Processi di leganti e hwbinder

Un visualizzatore systrace visualizza le transazioni come segue:

Figura 1. Visualizzazione systrace dei processi leganti.

Nell'esempio sopra:

  • I quattro (4) processi schd-dbg sono processi client.
  • I quattro (4) processi di binder sono processi server (il nome inizia con Binder e termina con un numero progressivo).
  • Un processo client è sempre associato a un processo server, dedicato al suo client.
  • Tutte le coppie di processi client-server sono programmate indipendentemente dal kernel contemporaneamente.

Nella CPU 1, il kernel del sistema operativo esegue il client per inviare la richiesta. Quindi utilizza la stessa CPU ogni volta che è possibile per riattivare un processo del server, gestire la richiesta e tornare al contesto dopo il completamento della richiesta.

Throughput vs. latenza

In una transazione perfetta, in cui i processi client e server cambiano senza soluzione di continuità, i test di velocità effettiva e di latenza non producono messaggi sostanzialmente diversi. Tuttavia, quando il kernel del sistema operativo gestisce una richiesta di interruzione (IRQ) dall'hardware, in attesa di blocchi o semplicemente scegliendo di non gestire immediatamente un messaggio, può formarsi una bolla di latenza.

Figura 2. Bolla di latenza dovuta a differenze nella velocità effettiva e nella latenza.

Il test della velocità effettiva genera un gran numero di transazioni con dimensioni del payload diverse, fornendo una buona stima del tempo di transazione regolare (nella migliore delle ipotesi) e della velocità massima che il raccoglitore può raggiungere.

Al contrario, il test di latenza non esegue azioni sul payload per ridurre al minimo i tempi di transazione regolari. Possiamo utilizzare il tempo di transazione per stimare l'overhead del raccoglitore, effettuare statistiche per il caso peggiore e calcolare il rapporto delle transazioni la cui latenza rispetta una scadenza specificata.

Gestire le inversioni prioritarie

Si verifica un'inversione di priorità quando un thread con priorità più alta è logicamente in attesa di un thread con priorità inferiore. Le applicazioni in tempo reale (RT) presentano un problema di inversione prioritaria:

Figura 3. Inversione di priorità nelle applicazioni in tempo reale.

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 problema. Nei casi in cui il framework Android necessita della pianificazione RT per garantire il privilegio di 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 binder):

Figura 4. Inversione di priorità, thread in tempo reale bloccati.

Per evitare blocchi, è possibile utilizzare l'ereditarietà prioritaria per convertire temporaneamente il thread Binder in un thread RT quando soddisfa 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 ; ulteriori thread RT potrebbero dover attendere (e quindi perdere le loro scadenze) se tutte le CPU sono prese da altri thread RT.

Per risolvere tutte le possibili inversioni prioritarie, è possibile utilizzare l'ereditarietà prioritaria sia per il raccoglitore che per l'hwbinder. Tuttavia, poiché il raccoglitore è ampiamente utilizzato in tutto il sistema, abilitare l'ereditarietà prioritaria per le transazioni del raccoglitore potrebbe spammare il sistema con più thread RT di quanti ne possa servire.

Esecuzione di test di velocità effettiva

Il test della velocità effettiva viene eseguito sulla velocità effettiva della transazione binder / hwbinder. In un sistema che 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à system/libhwbinder/vts/performance/Benchmark_binder.cpp raccoglitore è in system/libhwbinder/vts/performance/Benchmark_binder.cpp .
  • Il test della velocità effettiva di hwbinder è in system/libhwbinder/vts/performance/Benchmark.cpp .

Risultati del test

Esempio di risultati del test della velocità effettiva 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 quando le CPU sono programmate per il test.
  • Le iterazioni indicano il numero di volte in cui la funzione di test è stata eseguita.

Ad esempio, per un payload a 8 byte:

BM_sendVec_binderize/8         69974 ns      32700 ns      21296

... il throughput massimo che il raccoglitore può raggiungere è calcolato come:

Throughput MAX con payload a 8 byte = (8 * 21296) / 69974 ~ = 2.423 b / ns ~ = 2.268 Gb / s

Opzioni di test

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"
    },
   ….
}

Esecuzione di test di latenza

Il test di latenza misura il tempo impiegato dal client per iniziare l'inizializzazione della transazione, passare al processo del server per la gestione e ricevere il risultato. Il test cerca anche comportamenti noti dello scheduler che possono influire negativamente sulla latenza delle transazioni, come uno scheduler che non supporta l'ereditarietà prioritaria o onora il flag di sincronizzazione.

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

Risultati del test

I risultati (in .json) mostrano le statistiche relative alla latenza media / migliore / peggiore e al numero di scadenze mancate.

Opzioni di prova

I test di latenza accettano le seguenti opzioni:

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

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

Specifica delle iterazioni

Esempio con un gran numero 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"
}

Questi risultati del 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); la maggior parte delle transazioni dovrebbe soddisfare questo valore.
"I": 10000
Una singola iterazione di test include due (2) transazioni:
  • Una transazione per priorità normale ( CFS other )
  • Una transazione per priorità in tempo reale ( RT-fifo )
5000 iterazioni equivalgono a un totale di 10000 transazioni.
"S": 9352
9352 delle 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 transazioni miss alla scadenza, rendendo il rapporto di meetR ( meetR ) 0.9996.
"fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
Simile ad other_ms , ma per transazioni emesse dal client con priorità rt_fifo . È probabile (ma non necessario) che il file fifo_ms abbia un risultato migliore rispetto ad other_ms , con valori avg e wst più meetR e un meetR più meetR (la differenza può essere ancora più significativa con il carico in background).

Nota: il caricamento in background può influire sul risultato della velocità other_ms e sulla tupla other_ms nel test di latenza. Solo i fifo_ms possono mostrare risultati simili purché il carico in background abbia una priorità inferiore a RT-fifo .

Specifica dei valori di coppia

Ogni processo client è associato a 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 fintanto che il flag SYNC è honor .

Assicurarsi che il sistema non sia sovraccarico! 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 una pressione maggiore, usare -pair #cpu-1 (o -pair #cpu con cautela). Il test usando -pair n con n > #cpu sovraccarica il sistema e genera informazioni inutili.

Specifica dei valori di scadenza

Dopo test approfonditi dello scenario utente (esecuzione del test di latenza su un prodotto qualificato), abbiamo stabilito che 2,5ms è il termine da rispettare. Per le nuove applicazioni con requisiti più elevati (come 1000 foto / secondo), questo valore di scadenza cambierà.

Specifica dell'output dettagliato

L'uso dell'opzione -v mostra 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 nella CPU:1 con pid 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 nella CPU:0 per eseguirlo e lo sincronizza con la stessa CPU con il suo client.
  • Il secondo chiamante della transazione ha una priorità SCHED_OTHER . Il server SCHED_OTHER downgrade stesso e SCHED_OTHER il chiamante con priorità SCHED_OTHER .

Utilizzo di trace per il debug

È possibile specificare l'opzione -trace per eseguire il debug dei problemi di latenza. Se utilizzato, il test di latenza interrompe la registrazione del tracelog nel momento in cui viene rilevata una latenza errata. 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 è generalmente più lenta della modalità userdebug.
  • Quadro In che modo il servizio framework utilizza ioctl per configurare il raccoglitore?
  • Driver del raccoglitore . Il conducente supporta il blocco a grana fine? Contiene tutte le patch per girare le prestazioni?
  • Versione del kernel . Migliore è la capacità in tempo reale del kernel, migliori saranno i risultati.
  • Config . Kernel La configurazione del kernel contiene configurazioni DEBUG come DEBUG_PREEMPT e DEBUG_SPIN_LOCK ?
  • Schedulatore del kernel . Il kernel ha uno scheduler Energy-Aware (EAS) o Heterogeneous Multi-Processing (HMP)? Alcuni driver del kernel (driver cpu-freq , driver cpu-idle , cpu-hotplug , ecc.) Influiscono sullo scheduler?