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 superficiels à 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 de l'application, par exemple depuis un DSP

Aussi précise que puisse être la liste de 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 produit généralement, c'est qu'un schéma de tampon donné est implémenté et testé, mais pendant le test, un sous-dépassement ou un dépassement audio est entendu sous la forme d'un « clic » ou d'un « pop ». Pour compenser, le concepteur du système augmente ensuite la taille ou le nombre de tampons. Cela a pour résultat souhaité d'éliminer les sous-exécutions ou les dépassements, mais cela a également pour effet secondaire indésirable d'augmenter la latence. Pour plus d'informations sur les tailles de tampon, consultez la vidéo Latence audio : tailles de tampon .

Une meilleure approche consiste à comprendre les causes des sous-consommations 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 comprennent :

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

Planification Linux CFS et SCHED_FIFO

Le Linux CFS est conçu pour être équitable face aux charges de travail concurrentes partageant une ressource CPU commune. Cette équité est représentée par un paramètre sympa par thread. La valeur la plus intéressante va de -19 (le temps le moins agréable ou le plus de temps CPU alloué) à 20 (le plus beau ou le 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 « équitable » 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 retirer le processeur d'un thread avec une qualité numériquement faible vers un thread avec une qualité numériquement élevée. Dans le cas de l'audio, cela peut entraîner un sous-dépassement ou un dépassement.

La solution évidente est d'é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 politique SCHED_FIFO . Les priorités SCHED_FIFO disponibles vont de 1 à 99. Les threads audio s'exécutent en 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é plus élevée. Nous vous recommandons d'utiliser la priorité 1 autant que possible et de réserver les priorités 4 à 99 pour les threads dont l'exécution est garantie dans un laps de temps limité, qui s'exécutent avec une période plus courte que la période des threads audio et qui sont connus pour ne pas interférer avec la planification. de fils audio.

Planification à taux monotone

Pour plus d'informations sur la théorie de l'attribution de priorités fixes, consultez l'article Wikipédia Planification monotonique (RMS). Un point clé est que les priorités fixes doivent être attribuées strictement en fonction de la période, avec des priorités plus élevées 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 d'exécution maximale et le nombre maximal de calculs 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 nombre de calculs illimité par exécution), alors il ne doit pas se voir attribuer une priorité fixe car cela serait incompatible avec la planification de véritables threads périodiques. .

Inversion de priorité

L'inversion de priorité est un mode de défaillance classique des systèmes temps réel, dans lequel une tâche de priorité supérieure est bloquée pendant une durée illimitée en attendant qu'une tâche de priorité inférieure libère une ressource telle qu'un (état partagé protégé par) un mutex . Voir l'article « Éviter l'inversion de priorité » pour connaître les techniques permettant de l'atténuer.

Latence de planification

La latence de planification est le temps entre le moment où un thread est 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 entraîne des problèmes audio. Une longue latence de planification est plus susceptible de se produire lors des 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 l'ajustement de la fréquence et de la tension d'horloge du processeur. .

Interruptions

Dans de nombreuses conceptions, le CPU 0 gère 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 (DMA) audio. Concevez des gestionnaires d'interruptions pour terminer rapidement et reporter 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 la CPU 0 pendant une longue période a le même résultat, en retardant le traitement des interruptions audio. Des temps de désactivation d'interruption longs se produisent généralement en attendant un verrou tournant du noyau. Examinez ces verrous rotatifs pour vous assurer qu’ils sont limités.

Alimentation, performances et gestion thermique

La gestion de l'énergie 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 de la fréquence
  • noyau dynamique permettant
  • commutation de cluster
  • contrôle de puissance
  • connexion à chaud (échange à chaud)
  • différents modes de veille (arrêt, arrêt, veille, suspension, etc.)
  • migration de processus
  • 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 applicatif. Ces arrêts de travail peuvent interférer avec l'audio, une telle gestion doit donc ê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, il est plus important d’éviter des dommages permanents que l’audio !

Noyaux de sécurité

Un noyau de sécurité pour la gestion des droits numériques (DRM) peut fonctionner sur le(s) même(s) cœur(s) de processeur d'application que ceux utilisés pour le noyau principal du système d'exploitation et le code d'application. Tout moment pendant lequel une opération du noyau de sécurité est active sur un cœur constitue en réalité un arrêt du travail ordinaire qui s'exécuterait normalement sur ce cœur. Cela peut notamment 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 provoqué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 être observé. Les noyaux de sécurité doivent être conçus pour un arrêt de travail acceptable dans le pire des cas lorsque l'audio est actif.