Valutazione delle prestazioni

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

Sono disponibili due indicatori di prestazione visibili all'utente:

  • Prestazioni prevedibili e percepibili . L'interfaccia utente (UI) perde fotogrammi o esegue il rendering costantemente a 60 FPS? L'audio viene riprodotto senza artefatti o scoppiettii? Quanto dura il ritardo tra il tocco dello schermo da parte dell'utente e la visualizzazione dell'effetto sul display?
  • Tempo necessario per operazioni più lunghe (come l'apertura di applicazioni).

Il primo è più evidente del secondo. Gli utenti in genere notano jank ma non saranno in grado di distinguere il tempo di avvio dell'applicazione da 500 ms a 600 ms a meno che non guardino due dispositivi fianco a fianco. La latenza del tocco è immediatamente percepibile e contribuisce in modo significativo alla percezione di un dispositivo.

Di conseguenza, in un dispositivo veloce, la pipeline dell'interfaccia utente è l'elemento più importante nel sistema oltre a ciò che è necessario per mantenere funzionale la pipeline dell'interfaccia utente. Ciò significa che la pipeline dell'interfaccia utente dovrebbe anticipare qualsiasi altro lavoro non necessario per un'interfaccia utente fluida. Per mantenere un'interfaccia utente fluida, la sincronizzazione in background, il recapito delle notifiche e attività simili devono essere ritardate se è possibile eseguire il lavoro dell'interfaccia utente. È accettabile sacrificare le prestazioni di operazioni più lunghe (runtime HDR+, avvio dell'applicazione, ecc.) per mantenere un'interfaccia utente fluida.

Capacità vs jitter

Quando si considerano le prestazioni del dispositivo, la capacità e il jitter sono due parametri significativi.

Capacità

La capacità è la quantità totale di alcune risorse che il dispositivo possiede in un certo periodo di tempo. Possono trattarsi di risorse CPU, risorse GPU, risorse I/O, risorse di rete, larghezza di banda della memoria o qualsiasi metrica simile. Quando si esaminano le prestazioni dell'intero sistema, può essere utile astrarre i singoli componenti e assumere un'unica metrica che determina le prestazioni (soprattutto quando si ottimizza 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 informatiche online. La modifica della frequenza della CPU/GPU è il mezzo principale per modificare la capacità, ma ce ne sono altri, come 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 nel consumo energetico.

La capacità richiesta in un dato momento è determinata in gran parte dall'applicazione in esecuzione. Di conseguenza, la piattaforma può fare poco per adattare 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).

Jitter

Mentre la capacità richiesta per un carico di lavoro è facile da vedere, il jitter è un concetto più nebuloso. Per una buona introduzione al jitter come impedimento ai sistemi veloci, fare riferimento a IL CASO DELLA MANCANTE PRESTAZIONE DEL SUPERCOMPUTER: RAGGIUNGERE PRESTAZIONI OTTIMALI SUGLI 8.192 PROCESSORI DI ASCl 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 dei 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 del lavoro percepibile. Spesso è il lavoro che deve essere eseguito, ma potrebbe non avere requisiti temporali rigidi che ne impongano l'esecuzione in un momento particolare. Poiché è casuale, è estremamente difficile confutare l'esistenza del jitter per un dato carico di lavoro. È anche estremamente difficile dimostrare che una fonte nota di jitter sia stata la causa di un particolare problema di prestazioni. Gli strumenti più comunemente utilizzati per diagnosticare le cause del jitter (come il tracciamento o il logging) possono introdurre il proprio jitter.

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

  • Ritardo dello scheduler
  • Gestori di interruzioni
  • Codice driver in esecuzione per troppo tempo con prelazione o interruzioni disabilitati
  • Softirq di lunga durata
  • Conflitto di blocco (applicazione, framework, driver del kernel, blocco del raccoglitore, blocco mmap)
  • Conflitto tra 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 l'interfaccia utente nelle code di lavoro in cui potrebbe subire ritardi
  • Transizioni di inattività della CPU
  • Registrazione
  • Ritardi I/O
  • Creazione di processi non necessari (ad esempio, trasmissioni CONNECTIVITY_CHANGE)
  • Threshing della cache della pagina causato da memoria libera insufficiente

La quantità di tempo richiesta per un dato periodo di jitter può diminuire o meno all'aumentare della capacità. Ad esempio, se un driver lascia gli interrupt disabilitati mentre attende una lettura da un bus i2c, ci vorrà un periodo di 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, i processori più veloci solitamente non migliorano le prestazioni in situazioni limitate dal jitter.

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

Consumo di memoria

Il consumo di memoria è tradizionalmente ritenuto responsabile delle scarse prestazioni. Sebbene il consumo in sé non sia un problema di prestazioni, può causare jitter a causa di un basso sovraccarico del memorykiller, di riavvii del servizio e di esaurimento della cache delle pagine. Ridurre il consumo di memoria può evitare le cause dirette di scarse prestazioni, ma potrebbero esserci altri miglioramenti mirati che evitano anche quelle cause (ad esempio, bloccando il framework per evitare che venga paginato quando verrà paginato subito dopo).

Analisi delle prestazioni iniziali del dispositivo

Partire da un sistema funzionale ma con scarse prestazioni e tentare di correggere il comportamento del sistema esaminando casi individuali di scarse prestazioni visibili all'utente non è una strategia valida. Poiché le scarse prestazioni di solito non sono facilmente riproducibili (ad esempio, jitter) o sono un problema dell'applicazione, troppe variabili nell'intero sistema impediscono a questa strategia di essere efficace. Di conseguenza, è molto facile identificare erroneamente le cause e apportare miglioramenti minori perdendo opportunità sistemiche per correggere le prestazioni in tutto il sistema.

Utilizzare invece il seguente approccio generale quando si avvia un nuovo dispositivo:

  1. Ottieni l'avvio del sistema sull'interfaccia utente con tutti i driver in esecuzione e alcune impostazioni di base del regolatore di frequenza (se modifichi le impostazioni del regolatore di frequenza, ripeti tutti i passaggi seguenti).
  2. Assicurati che il kernel supporti il ​​tracepoint sched_blocked_reason e altri tracepoint nella pipeline di visualizzazione che indicano quando il frame viene consegnato al display.
  3. Prendi lunghe tracce dell'intera pipeline dell'interfaccia utente (dalla ricezione dell'input tramite un IRQ alla scansione finale) durante l'esecuzione di un carico di lavoro leggero e coerente (ad esempio, UiBench o il test della palla in TouchLatency) .
  4. Correggi i cali di frame rilevati nel carico di lavoro leggero e coerente.
  5. Ripeti i passaggi 3-4 finché non riesci a eseguire senza fotogrammi persi per più di 20 secondi alla volta.
  6. Passa ad altre fonti di cianfrusaglie visibili all'utente.

Altre semplici cose che puoi fare all'inizio dell'avvio del dispositivo includono:

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

    Questi eventi rimangono abilitati quando esegui systrace in modo da poter visualizzare informazioni aggiuntive nella traccia relativa alla pipeline di visualizzazione (MDSS) nella sezione mdss_fb0 . Sui SOC Qualcomm, non vedrai alcuna informazione aggiuntiva sulla GPU nella visualizzazione systrace standard, ma i risultati sono presenti nella traccia stessa (per i dettagli, vedi Comprendere la systrace ).

    Ciò che desideri da questo tipo di tracciamento del display è un singolo evento che indichi direttamente che un fotogramma è stato consegnato al display. Da lì, puoi determinare se hai raggiunto con successo il frame time; se l'evento X n si verifica meno di 16,7 ms dopo l'evento X n-1 (assumendo un display a 60 Hz), allora sai di non aver fatto jank. Se il tuo SOC non fornisce tali segnali, collabora con il tuo fornitore per ottenerli. Il debug del jitter è estremamente difficile senza un segnale definitivo di completamento del frame.

Utilizzo di benchmark sintetici

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

In base alle esperienze con i SOC, le differenze nelle prestazioni dei benchmark sintetici tra i SOC non sono correlate con una differenza simile nelle prestazioni percepibili dell'interfaccia utente (numero di fotogrammi persi, tempo di fotogramma al 99° percentile, ecc.). I benchmark sintetici sono benchmark basati esclusivamente sulla capacità; il jitter influisce sulle prestazioni misurate di questi benchmark solo rubando tempo all'operazione di massa del benchmark. Di conseguenza, i punteggi dei benchmark sintetici sono per lo più irrilevanti come metrica delle prestazioni percepite dall’utente.

Considera due SOC che eseguono Benchmark X che esegue il rendering di 1000 fotogrammi dell'interfaccia utente e riporta il tempo di rendering totale (il punteggio più basso è migliore).

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

Se il benchmark fosse indicativo delle prestazioni effettive dell'interfaccia utente, SOC 2 sarebbe inutilizzabile. Supponendo una frequenza di aggiornamento di 60 Hz, SOC 2 avrebbe un fotogramma scadente ogni 1,5 s di funzionamento. Nel frattempo, il SOC 1 (il SOC più lento secondo il Benchmark X) sarebbe perfettamente fluido.

Utilizzo delle segnalazioni di bug

Le segnalazioni di bug sono talvolta utili per l'analisi delle prestazioni, ma poiché sono così pesanti, raramente sono utili per il debug di problemi sporadici di anomalia. Potrebbero fornire alcuni suggerimenti su cosa stava facendo il sistema in un dato momento, soprattutto se il jank riguardava una transizione dell'applicazione (che viene registrata in una segnalazione di bug). Le segnalazioni di bug possono anche indicare quando c'è qualcosa di più ampio che non va nel sistema che potrebbe ridurne la capacità effettiva (come la limitazione termica o la frammentazione della memoria).

Utilizzando TouchLatency

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

Il test della palla che rimbalza è esattamente semplice come sembra: una palla rimbalza sullo schermo per sempre, indipendentemente dall'input dell'utente. Di solito è anche il test di gran lunga più difficile da eseguire perfettamente, ma più si avvicina al funzionamento senza perdita di fotogrammi, migliore sarà il tuo dispositivo. Il test della palla che rimbalza è difficile perché è un carico di lavoro banale ma perfettamente coerente che funziona con un clock molto basso (questo presuppone che il dispositivo abbia un regolatore di frequenza; se invece il dispositivo funziona con clock fissi, downclock della CPU/GPU quasi al minimo quando si esegue il test della palla rimbalzante per la prima volta). Man mano che il sistema entra in quiescenza e gli orologi si avvicinano allo stato di inattività, il tempo richiesto da CPU/GPU per frame aumenta. Puoi guardare la palla e vedere le cose che vanno male, e sarai anche in grado di vedere i fotogrammi persi in systrace.

Poiché il carico di lavoro è così coerente, è possibile identificare la maggior parte delle fonti di jitter molto più facilmente rispetto alla maggior parte dei carichi di lavoro visibili all'utente monitorando esattamente ciò che è in esecuzione sul sistema durante ciascun fotogramma mancato anziché la pipeline dell'interfaccia utente. I clock inferiori amplificano gli effetti del jitter rendendo più probabile che qualsiasi jitter causi una perdita di fotogramma. Di conseguenza, più TouchLatency si avvicina a 60FPS, meno è probabile che si verifichino comportamenti errati del sistema che causano jank sporadici e difficili da riprodurre in applicazioni più grandi.

Poiché il jitter è spesso (ma non sempre) invariante della velocità di clock, utilizzare un test eseguito con clock molto bassi per diagnosticare il jitter per i seguenti motivi:

  • Non tutto il jitter è invariante della velocità di clock; molte fonti consumano solo tempo della CPU.
  • Il governatore dovrebbe ottenere il tempo di frame medio vicino alla scadenza cronometrando il tempo, in modo che il tempo trascorso a eseguire lavori non dell'interfaccia utente possa spingerlo oltre il limite fino a far cadere un frame.