Contributeurs à la latence audio

Cette page se concentre sur les contributeurs à la latence de sortie, mais une discussion similaire s'applique à la latence d'entrée.

En supposant que les circuits analogiques ne contribuent pas de manière significative, les principaux contributeurs au niveau de la surface à la latence audio sont les suivants :

  • Application
  • Nombre total de tampons dans le pipeline
  • Taille de chaque tampon, en images
  • Latence supplémentaire après le processeur d'application, par exemple à partir d'un DSP

Aussi précise que puisse être la liste des contributeurs ci-dessus, elle est également trompeuse. La raison en est que le nombre et la taille du tampon sont plus un effet qu'une cause . Ce qui se passe généralement, c'est qu'un schéma de tampon donné est implémenté et testé, mais pendant les tests, une sous-utilisation ou un dépassement audio est entendu comme un "clic" ou un "pop". Pour compenser, le concepteur du système augmente alors les tailles de mémoire tampon ou le nombre de mémoires tampons. Cela a pour résultat souhaité d'éliminer les sous-utilisations ou les dépassements, mais cela a également l'effet secondaire indésirable d'augmenter la latence. Pour plus d'informations sur les tailles de mémoire tampon, consultez la vidéo Latence audio : tailles de mémoire tampon .

Une meilleure approche consiste à comprendre les causes des sous-utilisations et des dépassements, puis à les corriger. Cela élimine les artefacts audibles et peut permettre des tampons encore plus petits ou moins nombreux et ainsi réduire la latence.

D'après notre expérience, les causes les plus courantes de sous-utilisation et de dépassement incluent :

  • Linux CFS (planificateur entièrement équitable)
  • threads de haute priorité avec planification SCHED_FIFO
  • inversion de priorité
  • longue latence de planification
  • gestionnaires d'interruptions de longue durée
  • temps de désactivation d'interruption longue
  • gestion de l'alimentation
  • noyaux de sécurité

Ordonnancement Linux CFS et SCHED_FIFO

Le CFS Linux est conçu pour être équitable vis-à-vis des charges de travail concurrentes partageant une ressource CPU commune. Cette équité est représentée par un paramètre nice par thread. La valeur de nice va de -19 (moins agréable, ou plus de temps CPU alloué) à 20 (plus agréable, ou moins de temps CPU alloué). En général, tous les threads avec une valeur nice donnée reçoivent un temps CPU approximativement égal et les threads avec une valeur nice numériquement inférieure doivent s'attendre à recevoir plus de temps CPU. Cependant, le CFS n'est "passable" que sur des périodes d'observation relativement longues. Sur des fenêtres d'observation à court terme, CFS peut allouer la ressource CPU de manière inattendue. Par exemple, cela peut éloigner le processeur d'un thread avec une gentillesse numériquement faible vers un thread avec une gentillesse numériquement élevée. Dans le cas de l'audio, cela peut entraîner une sous-utilisation ou un dépassement.

La solution évidente consiste à éviter CFS pour les threads audio hautes performances. À partir d'Android 4.1, ces threads utilisent désormais la politique de planification SCHED_FIFO plutôt que la politique de planification SCHED_NORMAL (également appelée SCHED_OTHER ) mise en œuvre par CFS.

Priorités SCHED_FIFO

Bien que les threads audio hautes performances utilisent désormais SCHED_FIFO , ils sont toujours sensibles à d'autres threads SCHED_FIFO de priorité plus élevée. Il s'agit généralement de threads de travail du noyau, mais il peut également y avoir quelques threads utilisateur non audio avec la stratégie SCHED_FIFO . Les priorités SCHED_FIFO disponibles vont de 1 à 99. Les threads audio s'exécutent à la priorité 2 ou 3. Cela laisse la priorité 1 disponible pour les threads de priorité inférieure et les priorités 4 à 99 pour les threads de priorité supérieure. Nous vous recommandons d'utiliser la priorité 1 dans la mesure du possible et de réserver les priorités 4 à 99 pour les threads qui sont garantis de se terminer dans un laps de temps limité, s'exécutent avec une période plus courte que la période des threads audio et sont connus pour ne pas interférer avec la planification. de fils audio.

Ordonnancement monotone

Pour plus d'informations sur la théorie de l'attribution des priorités fixes, consultez l'article de Wikipédia Ordonnancement monotone (RMS). Un point clé est que les priorités fixes doivent être attribuées strictement en fonction de la période, les priorités les plus élevées étant attribuées aux threads de périodes plus courtes, et non en fonction de «l'importance» perçue. Les threads non périodiques peuvent être modélisés comme des threads périodiques, en utilisant la fréquence maximale d'exécution et le calcul maximal par exécution. Si un thread non périodique ne peut pas être modélisé comme un thread périodique (par exemple, il pourrait s'exécuter avec une fréquence illimitée ou un calcul illimité par exécution), alors il ne doit pas se voir attribuer une priorité fixe car cela serait incompatible avec la planification de vrais threads périodiques .

Inversion prioritaire

L'inversion de priorité est un mode de défaillance classique des systèmes en temps réel, où une tâche de priorité supérieure est bloquée pendant un temps illimité en attendant qu'une tâche de priorité inférieure libère une ressource telle que (état partagé protégé par) un mutex . Voir l'article " Eviter l'inversion de priorité " pour les techniques pour l'atténuer.

Latence de planification

La latence de planification est le temps entre le moment où un thread devient prêt à s'exécuter et le moment où le changement de contexte résultant se termine afin que le thread s'exécute réellement sur un processeur. Plus la latence est courte, mieux c'est, et tout dépassement de deux millisecondes cause des problèmes pour l'audio. Une longue latence de planification est plus susceptible de se produire pendant les transitions de mode, telles que l'activation ou l'arrêt d'un processeur, la commutation entre un noyau de sécurité et le noyau normal, le passage du mode pleine puissance au mode basse consommation ou le réglage de la fréquence et de la tension d'horloge du processeur. .

Interruptions

Dans de nombreuses conceptions, le processeur 0 traite toutes les interruptions externes. Ainsi, un gestionnaire d'interruptions de longue durée peut retarder d'autres interruptions, en particulier les interruptions d'achèvement d'accès direct à la mémoire audio (DMA). Concevez des gestionnaires d'interruptions pour qu'ils se terminent rapidement et reportent les travaux longs à un thread (de préférence un thread CFS ou un thread SCHED_FIFO de priorité 1).

De manière équivalente, la désactivation des interruptions sur le CPU 0 pendant une longue période a le même résultat de retarder le traitement des interruptions audio. De longs temps de désactivation des interruptions se produisent généralement lors de l'attente d'un verrou tournant du noyau. Passez en revue ces verrous tournants pour vous assurer qu'ils sont limités.

Alimentation, performances et gestion thermique

La gestion de l'alimentation est un terme général qui englobe les efforts visant à surveiller et à réduire la consommation d'énergie tout en optimisant les performances. La gestion thermique et le refroidissement des ordinateurs sont similaires mais cherchent à mesurer et à contrôler la chaleur pour éviter les dommages dus à un excès de chaleur. Dans le noyau Linux, le gouverneur du processeur est responsable de la politique de bas niveau, tandis que le mode utilisateur configure la politique de haut niveau. Les techniques utilisées comprennent :

  • mise à l'échelle dynamique de la tension
  • mise à l'échelle dynamique des fréquences
  • activation du noyau dynamique
  • commutation de cluster
  • alimentation électrique
  • connexion à chaud (échange à chaud)
  • divers modes de veille (halt, stop, idle, suspend, etc.)
  • processus de migration
  • affinité du processeur

Certaines opérations de gestion peuvent entraîner des "arrêts de travail" ou des périodes pendant lesquelles aucun travail utile n'est effectué par le processeur d'application. Ces arrêts de travail peuvent interférer avec l'audio, de sorte qu'une telle gestion doit être conçue pour un arrêt de travail acceptable dans le pire des cas pendant que l'audio est actif. Bien sûr, lorsque l'emballement thermique est imminent, éviter les dommages permanents est plus important que l'audio !

Noyaux de sécurité

Un noyau de sécurité pour la gestion des droits numériques (DRM) peut s'exécuter sur le ou les mêmes cœurs de processeur d'application que ceux utilisés pour le noyau du système d'exploitation principal et le code d'application. Chaque fois qu'une opération du noyau de sécurité est active sur un cœur, il s'agit en fait d'un arrêt du travail ordinaire qui devrait normalement s'exécuter sur ce cœur. En particulier, cela peut inclure des travaux audio. De par sa nature, le comportement interne d'un noyau de sécurité est impénétrable depuis les couches de niveau supérieur, et donc toute anomalie de performances causée par un noyau de sécurité est particulièrement pernicieuse. Par exemple, les opérations du noyau de sécurité n'apparaissent généralement pas dans les traces de changement de contexte. Nous appelons cela "le temps sombre" - le temps qui s'écoule mais ne peut pas être observé. Les noyaux de sécurité doivent être conçus pour un arrêt de travail acceptable dans le pire des cas pendant que l'audio est actif.