Uscita:
Android 12 (livello API 31) - API Performance Hint
Android 13 (livello API 33) - Performance Hint Manager nell'API NDK
(Anteprima) Android 15 (DP1) - reportActualWorkDuration()
Con i suggerimenti sulle prestazioni della CPU, un gioco può influenzare le prestazioni dinamiche della CPU un comportamento più adatto alle sue esigenze. Sulla maggior parte dei dispositivi, Android si regola dinamicamente la velocità di clock della CPU e il tipo di core per un carico di lavoro in base alle esigenze precedenti. Se un carico di lavoro utilizza più risorse della CPU, la velocità di clock viene aumentata e viene poi spostato in un core più grande. Se il carico di lavoro utilizza meno risorse di risorse, mentre Android riduce l'allocazione delle risorse. Con ADPF, l'applicazione o un gioco può inviare un ulteriore indicatore sul suo rendimento e sulle relative scadenze. Questo consente al sistema di crescere in modo più aggressivo (migliorando le prestazioni) e riducendo l'orologio quando il carico di lavoro è completo (risparmio energetico).
Velocità orologio
Quando i dispositivi Android regolano dinamicamente la velocità di clock della CPU, la frequenza può
modificare le prestazioni del codice. Progettare un codice per gestire l'orologio dinamico
velocità è importante per massimizzare le prestazioni, mantenere una temperatura
e utilizzando l'energia in modo efficiente. Non puoi assegnare direttamente le frequenze CPU
nel codice dell'app. Di conseguenza, un modo comune per far funzionare le app
Le velocità di clock della CPU prevedono l'esecuzione di un loop di occupato in un thread in background in modo che il carico di lavoro
sembra più impegnativa. Si tratta di una cattiva pratica perché spreca energia e aumenta
il carico termico sul dispositivo quando l'app non sta effettivamente utilizzando la
Google Cloud. L'API CPU PerformanceHint
è progettata per risolvere questo problema. Di
per comunicare al sistema la durata effettiva del lavoro
e la durata prevista
Android potrà ottenere una panoramica delle esigenze di CPU dell'app e allocare
risorse in modo efficiente. Ciò porterà a prestazioni ottimali con un'alimentazione efficiente
e il livello di consumo eccessivo.
Tipi di core
I tipi di core della CPU su cui viene eseguito il gioco sono un'altra importante prestazione fattore. I dispositivi Android spesso cambiano il core della CPU assegnato a un thread dinamicamente in base al comportamento recente del carico di lavoro. L'assegnazione dei core della CPU è ancora più complessi su SoC con più tipi di core. Su alcuni di questi dispositivi, la maggiore I core possono essere utilizzati solo per breve tempo senza entrare in un ambiente termicamente insostenibile stato.
Il tuo gioco non dovrebbe provare a impostare l'affinità dei core della CPU per i seguenti motivi:
- Il miglior tipo di core per un carico di lavoro varia in base al modello di dispositivo.
- La sostenibilità dell'esecuzione di core più grandi varia in base al SoC e ai vari soluzioni termiche fornite da ciascun modello di dispositivo.
- L'impatto ambientale sullo stato termico può complicare ulteriormente il nucleo scelta. Ad esempio, il meteo o la custodia dello smartphone possono cambiare lo stato termico di un dispositivo.
- La selezione dei componenti principali non è in grado di supportare i nuovi dispositivi con prestazioni e prestazioni aggiuntive termiche. Di conseguenza, i dispositivi spesso ignorano il processore di un gioco di affinità.
Esempio di comportamento predefinito dello scheduler di Linux
.L'API PerformanceHint astrae più delle latenze di DVFS
.- .
- Se le attività devono essere eseguite su una CPU specifica, l'API PerformanceHint sa come eseguire a prendere questa decisione per tuo conto.
- Pertanto, non è necessario utilizzare l'affinità.
- I dispositivi hanno varie topologie: Le caratteristiche di potenza e temperatura sono troppo vari per essere esposti agli sviluppatori di app.
- Non puoi fare ipotesi sul sistema sottostante su cui stai eseguendo.
Soluzione
L'ADF fornisce la classe PerformanceHintManager
in modo che i giochi possano inviare suggerimenti sulle prestazioni ad Android per la velocità di clock della CPU e
tipo di core. Il sistema operativo può quindi decidere come utilizzare al meglio i suggerimenti in base al SoC e
soluzione termica del dispositivo. Se la tua app usa questa API insieme a termiche
monitoraggio dello stato, può fornire suggerimenti più consapevoli al sistema operativo anziché utilizzare
loop di traffico e altre tecniche di programmazione che possono causare la limitazione.
Ecco come un gioco utilizza i suggerimenti per le prestazioni:
- Crea sessioni di suggerimento per i thread chiave con un comportamento simile. Ad esempio:
- Il thread di rendering e le sue dipendenze richiedono una sessione
- In Cocos, il thread del motore principale e il thread di rendering hanno uno sessione
- In Unity, integra il plug-in del provider Android con prestazioni adattive
- In Unreal, integra il plug-in Unreal Adaptive Performance e utilizza Opzioni di scalabilità per supportare più livelli qualitativi
- I thread di I/O ricevono un'altra sessione
- I thread audio ricevono una terza sessione
- Il thread di rendering e le sue dipendenze richiedono una sessione
- Il gioco dovrebbe farlo presto, almeno 2 ms e preferibilmente più di 4 ms prima che una sessione richieda maggiori risorse di sistema.
- In ogni sessione di suggerimento, prevedi la durata necessaria per l'esecuzione di ogni sessione. La durata tipica equivale a un intervallo di frame, ma l'app può utilizzare una se il carico di lavoro non varia in modo significativo da un frame all'altro.
Ecco come mettere in pratica la teoria:
inizializza PerformanceHintManager e createHintSession
Fai utilizzare il gestore al servizio di sistema e crea una sessione hint per il tuo thread o un gruppo di thread che lavora sullo stesso carico di lavoro.
C++
int32_t tids[1];
tids[0] = gettid();
int64_t target_fps_nanos = getFpsNanos();
APerformanceHintManager* hint_manager = APerformanceHint_getManager();
APerformanceHintSession* hint_session =
APerformanceHint_createSession(hint_manager, tids, 1, target_fps_nanos);
Java
int[] tids = {
android.os.Process.myTid()
};
long targetFpsNanos = getFpsNanos();
PerformanceHintManager performanceHintManager =
(PerformanceHintManager) this.getSystemService(Context.PERFORMANCE_HINT_SERVICE);
PerformanceHintManager.Session hintSession =
performanceHintManager.createHintSession(tids, targetFpsNanos);
Imposta i thread, se necessario
Uscita:
Android 11 (livello API 34)
Utilizza la setThreads
funzione di PerformanceHintManager.Session
quando ci sono altri thread
che devono essere aggiunti in un secondo momento. Ad esempio, se crei il tuo thread relativo alla fisica
in un secondo momento e devi aggiungerlo alla sessione, puoi usare questa API setThreads
.
C++
auto tids = thread_ids.data();
std::size_t size = thread_ids_.size();
APerformanceHint_setThreads(hint_session, tids, size);
Java
int[] tids = new int[3];
// add all your thread IDs. Remember to use android.os.Process.myTid() as that
// is the linux native thread-id.
// Thread.currentThread().getId() will not work because it is jvm's thread-id.
hintSession.setThreads(tids);
Se hai come target livelli API più bassi, dovrai eliminare la sessione e ricreano una nuova sessione ogni volta che devi modificare gli ID thread.
Report durata lavoro effettiva
Monitora la durata effettiva necessaria per completare il lavoro in nanosecondi e genera un report al sistema al termine del lavoro in ogni ciclo. Ad esempio, se per i thread di rendering, chiamala per ogni frame.
Per ottenere in modo affidabile l'ora effettiva, utilizza:
C++
clock_gettime(CLOCK_MONOTONIC, &clock); // if you prefer "C" way from <time.h>
// or
std::chrono::high_resolution_clock::now(); // if you prefer "C++" way from <chrono>
Java
System.nanoTime();
Ad esempio:
C++
// All timings should be from `std::chrono::steady_clock` or `clock_gettime(CLOCK_MONOTONIC, ...)`
auto start_time = std::chrono::high_resolution_clock::now();
// do work
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count();
int64_t actual_duration = static_cast<int64_t>(duration);
APerformanceHint_reportActualWorkDuration(hint_session, actual_duration);
Java
long startTime = System.nanoTime();
// do work
long endTime = System.nanoTime();
long duration = endTime - startTime;
hintSession.reportActualWorkDuration(duration);
Aggiorna la durata di lavoro target quando necessario
Ogni volta che la durata di lavoro target cambia, ad esempio se il giocatore sceglie una
f/s target diverso, chiama updateTargetWorkDuration
per comunicare al sistema in modo che quest'ultimo possa regolare le risorse in base
al nuovo target. Non è necessario chiamarla a ogni frame e basta
e viene chiamato quando cambia la durata target.
C++
APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);
Java
hintSession.updateTargetWorkDuration(targetDuration);