Valutare il rendimento

Utilizza Simpleperf per valutare le prestazioni di un dispositivo. Simpleperf è uno strumento di profilazione nativo per app e processi nativi su Android. Utilizza CPU Profiler per esaminare l'utilizzo della CPU dell'app e l'attività dei thread in tempo reale.

Esistono due indicatori di rendimento visibili agli utenti:

  • Prestazioni prevedibili e percepibili. L'interfaccia utente (UI) perde frame o viene visualizzata in modo coerente a 60 FPS? L'audio viene riprodotto senza artefatti o scoppiettii? Quanto tempo intercorre tra il momento in cui l'utente tocca lo schermo e il momento in cui l'effetto viene visualizzato sul display?
  • Periodo di tempo necessario per operazioni più lunghe (ad esempio l'apertura di app).

Il primo è più evidente del secondo. In genere gli utenti notano i problemi di scattosità, ma non riescono a distinguere un tempo di avvio dell'app di 500 ms da uno di 600 ms, a meno che non stiano guardando due dispositivi uno accanto all'altro. La latenza al tocco è immediatamente percepibile e contribuisce in modo significativo alla percezione di un dispositivo.

Di conseguenza, in un dispositivo veloce, la pipeline della UI è la cosa più importante del sistema, a parte ciò che è necessario per mantenerla funzionale. Ciò significa che la pipeline dell'interfaccia utente deve dare la precedenza a qualsiasi altro lavoro non necessario per un'interfaccia utente fluida. Per mantenere un'interfaccia utente fluida, la sincronizzazione in background, la distribuzione delle notifiche e attività simili devono essere ritardate se è possibile eseguire il lavoro dell'interfaccia utente. È accettabile scambiare le prestazioni di operazioni più lunghe (runtime HDR+, avvio dell'app e così via) per mantenere un'interfaccia utente fluida.

Capacità e jitter

Quando si valuta il rendimento del dispositivo, capacità e jitter sono due metriche significative.

Capacità

La capacità è la quantità totale di una risorsa che il dispositivo possiede in un determinato periodo di tempo. Possono essere risorse CPU, GPU, I/O, di rete, larghezza di banda della memoria o qualsiasi metrica simile. Quando esamini il rendimento dell'intero sistema, può essere utile astrarre i singoli componenti e presupporre una singola metrica che determina il rendimento (soprattutto quando metti a punto un nuovo dispositivo perché i carichi di lavoro eseguiti su quel dispositivo sono probabilmente fissi).

La capacità di un sistema varia in base alle risorse di calcolo online. La modifica della frequenza della CPU/GPU è il mezzo principale per modificare la capacità, ma ce ne sono altri, ad esempio la modifica del numero di core della CPU online. Di conseguenza, la capacità di un sistema corrisponde al consumo energetico; la modifica della capacità comporta sempre una variazione simile del consumo energetico.

La capacità richiesta in un determinato momento è determinata in modo schiacciante dall'app in esecuzione. Di conseguenza, la piattaforma può fare poco per regolare la capacità richiesta per un determinato carico di lavoro e i mezzi per farlo sono limitati ai miglioramenti del runtime (framework Android, ART, Bionic, compilatore/driver GPU, kernel).

Tremolio

Mentre la capacità richiesta per un workload è facile da vedere, il jitter è un concetto più nebuloso. Per una buona introduzione al jitter come impedimento per i sistemi veloci, ti consigliamo di leggere l'articolo intitolato The Case of the Missing Supercomputer Performance: Achieving Optimal Performance on the 8,192 processors of ASCI Q. Si tratta di un'indagine sul motivo per cui il supercomputer ASCI Q non ha raggiunto le prestazioni previste ed è un'ottima introduzione all'ottimizzazione di sistemi di grandi dimensioni.

Questa pagina utilizza il termine jitter per descrivere ciò che il documento ASCI Q chiama rumore. Il jitter è il comportamento casuale del sistema che impedisce l'esecuzione di un lavoro percepibile. Spesso si tratta di un lavoro che deve essere eseguito, ma potrebbe non avere requisiti di tempistica rigorosi che ne determinano l'esecuzione in un momento particolare. Poiché è casuale, è estremamente difficile confutare l'esistenza di jitter per un determinato carico di lavoro. Inoltre, è estremamente difficile dimostrare che una fonte nota di jitter sia stata la causa di un particolare problema di rendimento. Gli strumenti più comunemente utilizzati per diagnosticare le cause del jitter (come il tracciamento o la registrazione) possono introdurre il proprio jitter.

Le fonti di jitter riscontrate nelle implementazioni reali di Android includono:

  • Ritardo scheduler
  • Gestori di interruzioni
  • Codice del driver in esecuzione troppo a lungo con prerilascio o interruzioni disabilitati
  • Softirq a lunga esecuzione
  • Conflitto blocco (app, framework, driver del kernel, blocco binder, blocco mmap)
  • Contesa di descrittori di file in cui un thread a bassa priorità mantiene il blocco su un file, impedendo l'esecuzione di un thread ad alta priorità
  • Esecuzione di codice critico per la UI in workqueue in cui potrebbe essere ritardato
  • Transizioni di inattività della CPU
  • Logging
  • Ritardi I/O
  • Creazione di processi non necessari (ad esempio, trasmissioni CONNECTIVITY_CHANGE)
  • Thrashing della cache di pagine causato da memoria libera insufficiente

Il tempo richiesto per un determinato periodo di jitter può diminuire o meno all'aumentare della capacità. Ad esempio, se un driver lascia gli interrupt disattivati mentre attende una lettura da un bus i2c, impiegherà un tempo fisso, indipendentemente dal fatto che la CPU sia a 384 MHz o 2 GHz. Aumentare la capacità non è una soluzione fattibile per migliorare le prestazioni quando è coinvolto il jitter. Di conseguenza, processori più veloci di solito non migliorano le prestazioni in situazioni con vincoli di jitter.

Infine, a differenza della capacità, il jitter è quasi interamente di competenza del fornitore del sistema.

Consumo di memoria

Il consumo di memoria è tradizionalmente considerato la causa delle prestazioni scarse. Sebbene il consumo in sé non sia un problema di prestazioni, può causare jitter tramite l'overhead di lowmemorykiller, i riavvii del servizio e il thrashing della cache della pagina. La riduzione del consumo di memoria può evitare le cause dirette di prestazioni scarse, ma potrebbero esserci altri miglioramenti mirati che evitano anche queste cause (ad esempio, il blocco del framework per impedirne il paging quando verrà eseguito poco dopo).

Analizzare il rendimento iniziale del dispositivo

Partire da un sistema funzionale ma con prestazioni scarse e tentare di correggere il comportamento del sistema esaminando i singoli casi di prestazioni scarse visibili agli utenti non è una strategia valida. Poiché il rendimento scarso di solito non è facilmente riproducibile (ovvero jitter) o un problema dell'app, troppe variabili nel sistema completo impediscono a questa strategia di essere efficace. Di conseguenza, è molto facile identificare erroneamente le cause e apportare miglioramenti minori, perdendo opportunità sistemiche per risolvere i problemi di rendimento in tutto il sistema.

Utilizza invece il seguente approccio generale quando configuri un nuovo dispositivo:

  1. Avvia il sistema nell'interfaccia utente con tutti i driver in esecuzione e alcune impostazioni di base del governor della frequenza (se modifichi le impostazioni del governor della frequenza, ripeti tutti i passaggi riportati di seguito).
  2. Assicurati che il kernel supporti il punto di traccia sched_blocked_reason e altri punti di traccia nella pipeline del display che indicano quando il frame viene inviato al display.
  3. Acquisisci tracce lunghe dell'intera pipeline della UI (dalla ricezione dell'input tramite un IRQ alla scansione finale) durante l'esecuzione di un workload leggero e coerente (ad esempio, UiBench o il test della palla in TouchLatency).
  4. Correggi i cali di frame rilevati nel workload leggero e coerente.
  5. Ripeti i passaggi 3-4 finché non riesci a correre senza frame persi per più di 20 secondi alla volta.
  6. Passa ad altre origini di jank visibili all'utente.

Altre semplici operazioni che puoi eseguire all'inizio della configurazione del dispositivo includono:

  • Assicurati che il kernel abbia la patch tracepoint sched_blocked_reason. Questo punto di traccia è abilitato con la categoria di traccia sched in systrace e fornisce la funzione responsabile della sospensione quando il thread entra in sospensione non interrompibile. È fondamentale per l'analisi delle prestazioni perché il sonno ininterrotto è un indicatore molto comune di jitter.
  • Assicurati di avere una traccia sufficiente per le pipeline di GPU e display. Sui SOC Qualcomm recenti, i punti di traccia vengono attivati utilizzando:
  • adb shell "echo 1 > /d/tracing/events/kgsl/enable"
    adb shell "echo 1 > /d/tracing/events/mdss/enable"
    

    Questi eventi rimangono attivi quando esegui systrace, in modo da poter visualizzare ulteriori informazioni nella traccia sulla pipeline di visualizzazione (MDSS) nella sezione mdss_fb0. Sui SOC Qualcomm, non vedrai ulteriori informazioni sulla GPU nella visualizzazione standard di systrace, ma i risultati sono presenti nella traccia stessa (per i dettagli, vedi Informazioni su systrace).

    Ciò che vuoi da questo tipo di tracciamento del display è un singolo evento che indichi direttamente che un frame è stato inviato al display. Da qui, puoi determinare se hai raggiunto correttamente il tempo di frame; se l'evento Xn si verifica meno di 16,7 ms dopo l'evento Xn-1 (supponendo un display a 60 Hz), allora sai che non si è verificato jank. Se il tuo SOC non fornisce questi segnali, collabora con il tuo fornitore per ottenerli. Il debug del jitter è estremamente difficile senza un segnale definitivo di completamento del frame.

Utilizzare benchmark sintetici

I benchmark sintetici sono utili per garantire la presenza delle funzionalità di base di un dispositivo. Tuttavia, trattare i benchmark come proxy per le prestazioni percepite del dispositivo non è utile.

In base all'esperienza con i SoC, le differenze nel rendimento dei benchmark sintetici tra i SoC non sono correlate a una differenza simile nel rendimento percepibile dell'interfaccia utente (numero di frame persi, tempo di frame al 99° percentile e così via). I benchmark sintetici sono benchmark solo di capacità; il jitter influisce sulle prestazioni misurate di questi benchmark solo sottraendo tempo all'operazione bulk del benchmark. Di conseguenza, i punteggi dei benchmark sintetici sono per lo più irrilevanti come metrica delle prestazioni percepite dagli utenti.

Considera due SOC che eseguono il benchmark X che esegue il rendering di 1000 frame dell'interfaccia utente e riporta il tempo di rendering totale (un punteggio inferiore è migliore).

  • SOC 1 esegue il rendering di ogni frame di Benchmark X in 10 ms e ottiene un punteggio di 10.000.
  • SOC 2 esegue il rendering del 99% dei frame in 1 ms,ma dell'1% dei frame in 100 ms e ottiene un punteggio di 19.900, un punteggio notevolmente migliore.

Se il benchmark è indicativo delle prestazioni effettive della UI, SOC 2 sarebbe inutilizzabile. Supponendo una frequenza di aggiornamento di 60 Hz, SOC 2 avrebbe un frame instabile ogni 1,5 secondi di funzionamento. Nel frattempo, SOC 1 (il SOC più lento secondo Benchmark X) sarebbe perfettamente fluido.

Utilizzare le segnalazioni di bug

A volte i report sui bug sono utili per l'analisi delle prestazioni, ma poiché sono molto pesanti, raramente sono utili per il debug di problemi di scatti sporadici. Possono fornire alcuni suggerimenti su cosa stava facendo il sistema in un determinato momento, soprattutto se il jank si è verificato durante una transizione dell'app (che viene registrata in una segnalazione di bug). I report sui bug possono anche indicare quando qualcosa non va nel sistema e potrebbe ridurne la capacità effettiva (ad esempio limitazione termica o frammentazione della memoria).

Utilizzare TouchLatency

Diversi esempi di comportamento anomalo provengono da TouchLatency, che è il carico di lavoro periodico preferito utilizzato per Pixel e Pixel XL. È disponibile all'indirizzo frameworks/base/tests/TouchLatency e ha due modalità: latenza del tocco e palla che rimbalza (per cambiare modalità, fai clic sul pulsante nell'angolo in alto a destra).

Il test della palla che rimbalza è semplice come sembra: una palla rimbalza sullo schermo all'infinito, indipendentemente dall'input dell'utente. In genere è anche di gran lunga il test più difficile da eseguire perfettamente, ma più si avvicina all'esecuzione senza frame persi, meglio sarà il tuo dispositivo. Il test della palla che rimbalza è difficile perché è un carico di lavoro banale ma perfettamente coerente che viene eseguito a una frequenza di clock molto bassa (si presuppone che il dispositivo abbia un governor di frequenza; se il dispositivo viene invece eseguito con clock fissi, riduci la frequenza di clock della CPU/GPU a un valore quasi minimo quando esegui il test della palla che rimbalza per la prima volta). Man mano che il sistema si spegne e gli orologi si avvicinano all'inattività, il tempo CPU/GPU richiesto per frame aumenta. Puoi guardare la palla e vedere le cose che si muovono a scatti e potrai vedere anche i frame mancanti in systrace.

Poiché il workload è molto coerente, puoi identificare la maggior parte delle fonti di jitter molto più facilmente rispetto alla maggior parte dei workload visibili agli utenti monitorando cosa viene eseguito esattamente sul sistema durante ogni frame mancante anziché la pipeline dell'interfaccia utente. Le frequenze di clock più basse amplificano gli effetti del jitter, rendendo più probabile che qualsiasi jitter causi un frame perso. Di conseguenza, più TouchLatency si avvicina a 60 FPS, meno è probabile che si verifichino comportamenti anomali del sistema che causano jank sporadici e difficili da riprodurre nelle app più grandi.

Poiché il jitter è spesso (ma non sempre) invariante alla velocità di clock, utilizza un test che venga eseguito a velocità di clock molto basse per diagnosticare il jitter per i seguenti motivi:

  • Non tutto il jitter è invariante alla velocità di clock; molte origini consumano solo tempo della CPU.
  • Il governor dovrebbe avvicinare il tempo medio del frame alla scadenza riducendo la velocità di clock, in modo che il tempo trascorso a eseguire operazioni non UI possa superare il limite e causare l'eliminazione di un frame.