Contributori alla latenza audio

Questa pagina si concentra sui fattori che contribuiscono alla latenza di output, ma una discussione simile si applica alla latenza di input.

Supponendo che i circuiti analogici non contribuiscano in modo significativo, i principali contributori a livello di superficie alla latenza audio sono i seguenti:

  • Applicazione
  • Numero totale di buffer nella pipeline
  • Dimensioni di ciascun buffer, in fotogrammi
  • Latenza aggiuntiva dopo il processore dell'app, ad esempio da un DSP

Per quanto accurato possa essere l'elenco dei contributori sopra riportato, è anche fuorviante. Il motivo è che il conteggio e la dimensione del buffer sono più un effetto che una causa . Ciò che accade di solito è che un determinato schema di buffer viene implementato e testato, ma durante il test, un sottoflusso o un superamento dell'audio viene percepito come un "clic" o un "pop". Per compensare, il progettista del sistema aumenta quindi le dimensioni del buffer o il conteggio dei buffer. Ciò ha il risultato desiderato di eliminare gli underrun o gli overrun, ma ha anche l'effetto collaterale indesiderato di aumentare la latenza. Per ulteriori informazioni sulle dimensioni del buffer, vedere il video Latenza audio: dimensioni del buffer .

Un approccio migliore consiste nel comprendere le cause dei superamenti e dei superamenti e quindi correggerli. Ciò elimina gli artefatti udibili e può consentire buffer ancora più piccoli o inferiori e quindi ridurre la latenza.

Nella nostra esperienza, le cause più comuni di superamenti e superamenti includono:

  • Linux CFS (programmatore completamente corretto)
  • thread ad alta priorità con pianificazione SCHED_FIFO
  • inversione di priorità
  • lunga latenza di pianificazione
  • gestori di interruzioni a lunga esecuzione
  • tempo di disabilitazione dell'interruzione lungo
  • gestione energetica
  • kernel di sicurezza

Pianificazione Linux CFS e SCHED_FIFO

Linux CFS è progettato per essere equo nei confronti dei carichi di lavoro concorrenti che condividono una risorsa CPU comune. Questa equità è rappresentata da un parametro nice per thread. Il valore nice varia da -19 (meno nice, o maggior tempo CPU allocato) a 20 (migliore, o minor tempo CPU allocato). In generale, tutti i thread con un dato valore nice ricevono approssimativamente lo stesso tempo di CPU e i thread con un valore nice numericamente inferiore dovrebbero aspettarsi di ricevere più tempo di CPU. Tuttavia, la CFS è “discreta” solo per periodi di osservazione relativamente lunghi. Nelle finestre di osservazione a breve termine, CFS può allocare le risorse della CPU in modi inaspettati. Ad esempio, potrebbe portare la CPU da un thread con niceness numericamente basso a un thread con niceness numericamente alto. Nel caso dell'audio, ciò può comportare un underrun o un overrun.

La soluzione ovvia è evitare CFS per thread audio ad alte prestazioni. A partire da Android 4.1, tali thread ora utilizzano la politica di pianificazione SCHED_FIFO anziché la politica di pianificazione SCHED_NORMAL (chiamata anche SCHED_OTHER ) implementata da CFS.

Priorità SCHED_FIFO

Sebbene i thread audio ad alte prestazioni ora utilizzino SCHED_FIFO , sono ancora suscettibili ad altri thread SCHED_FIFO con priorità più alta. Si tratta in genere di thread di lavoro del kernel, ma potrebbero esserci anche alcuni thread utente non audio con policy SCHED_FIFO . Le priorità SCHED_FIFO disponibili vanno da 1 a 99. I thread audio vengono eseguiti con priorità 2 o 3. Ciò lascia la priorità 1 disponibile per i thread con priorità inferiore e le priorità da 4 a 99 per i thread con priorità più alta. Ti consigliamo di utilizzare la priorità 1 quando possibile e di riservare le priorità da 4 a 99 per quei thread che sono garantiti per essere completati entro un periodo di tempo limitato, eseguiti con un periodo più breve del periodo dei thread audio e che sono noti per non interferire con la pianificazione di thread audio.

Schedulazione tariffaria monotona

Per ulteriori informazioni sulla teoria dell'assegnazione delle priorità fisse, consultare l'articolo di Wikipedia Rate-monotonic scheduling (RMS). Un punto chiave è che le priorità fisse dovrebbero essere assegnate rigorosamente in base al periodo, con priorità più elevate assegnate a thread di periodi più brevi, non in base all'"importanza" percepita. I thread non periodici possono essere modellati come thread periodici, utilizzando la massima frequenza di esecuzione e il massimo calcolo per esecuzione. Se un thread non periodico non può essere modellato come thread periodico (ad esempio potrebbe essere eseguito con frequenza illimitata o calcolo illimitato per esecuzione), allora non dovrebbe essergli assegnata una priorità fissa in quanto ciò sarebbe incompatibile con la pianificazione dei veri thread periodici .

Inversione di priorità

L'inversione della priorità è una classica modalità di errore dei sistemi in tempo reale, in cui un'attività con priorità più alta viene bloccata per un tempo illimitato in attesa che un'attività con priorità inferiore rilasci una risorsa come (stato condiviso protetto da) un mutex . Vedi l'articolo " Evitare l'inversione di priorità " per le tecniche per mitigarla.

Latenza di pianificazione

La latenza di pianificazione è il tempo che intercorre tra il momento in cui un thread diventa pronto per l'esecuzione e il momento in cui il cambio di contesto risultante viene completato in modo che il thread venga effettivamente eseguito su una CPU. Più breve è la latenza, meglio è, e qualsiasi valore superiore a due millisecondi causa problemi all'audio. È più probabile che si verifichi una lunga latenza di pianificazione durante le transizioni di modalità, come l'avvio o lo spegnimento di una CPU, il passaggio da un kernel di sicurezza a quello normale, il passaggio dalla modalità a piena potenza a quella a basso consumo o la regolazione della frequenza e della tensione del clock della CPU. .

Interrompe

In molti progetti, la CPU 0 gestisce tutti gli interrupt esterni. Pertanto un gestore di interruzioni di lunga esecuzione può ritardare altre interruzioni, in particolare interruzioni di completamento dell'accesso diretto alla memoria audio (DMA). Progettare gestori di interrupt per terminare rapidamente e rinviare il lavoro lungo a un thread (preferibilmente un thread CFS o un thread SCHED_FIFO con priorità 1).

Allo stesso modo, disabilitare gli interrupt sulla CPU 0 per un lungo periodo ha lo stesso risultato di ritardare la gestione degli interrupt audio. Tempi lunghi di disabilitazione dell'interrupt si verificano in genere durante l'attesa di uno spin lock del kernel. Esamina questi blocchi di rotazione per assicurarti che siano delimitati.

Potenza, prestazioni e gestione termica

La gestione dell'energia è un termine ampio che comprende gli sforzi per monitorare e ridurre il consumo energetico ottimizzando al tempo stesso le prestazioni. La gestione termica e il raffreddamento del computer sono simili ma cercano di misurare e controllare il calore per evitare danni dovuti al calore in eccesso. Nel kernel Linux, il governatore della CPU è responsabile della politica di basso livello, mentre la modalità utente configura la politica di alto livello. Le tecniche utilizzate includono:

  • scalabilità dinamica della tensione
  • scala dinamica della frequenza
  • abilitazione del nucleo dinamico
  • commutazione di cluster
  • gating di potenza
  • hotplug (sostituzione a caldo)
  • varie modalità di sospensione (arresto, arresto, inattività, sospensione, ecc.)
  • migrazione dei processi
  • affinità del processore

Alcune operazioni di gestione possono comportare "interruzioni del lavoro" o periodi durante i quali non viene svolto lavoro utile da parte dell'elaboratore dell'applicazione. Queste interruzioni del lavoro possono interferire con l'audio, pertanto tale gestione dovrebbe essere progettata per un'interruzione del lavoro accettabile nel caso peggiore mentre l'audio è attivo. Naturalmente, quando la fuga termica è imminente, evitare danni permanenti è più importante dell'audio!

Kernel di sicurezza

Un kernel di sicurezza per la gestione dei diritti digitali (DRM) può essere eseguito sugli stessi core del processore dell'applicazione utilizzati per il kernel del sistema operativo principale e il codice dell'applicazione. Ogni momento durante il quale un'operazione del kernel di sicurezza è attiva su un core costituisce effettivamente un'interruzione del lavoro ordinario che normalmente verrebbe eseguito su quel core. In particolare, ciò può includere lavori audio. Per sua natura, il comportamento interno di un kernel di sicurezza è imperscrutabile dai livelli di livello superiore e quindi qualsiasi anomalia prestazionale causata da un kernel di sicurezza è particolarmente dannosa. Ad esempio, le operazioni del kernel di sicurezza in genere non vengono visualizzate nelle tracce del cambio di contesto. Chiamiamo questo “tempo oscuro” – tempo che trascorre ma non può essere osservato. I kernel di sicurezza dovrebbero essere progettati per un'interruzione del lavoro accettabile nel caso peggiore mentre l'audio è attivo.