Esta página se centra en los contribuyentes a la latencia de salida, pero se aplica una discusión similar a la latencia de entrada.
Suponiendo que los circuitos analógicos no contribuyen significativamente, los principales contribuyentes a nivel de superficie a la latencia de audio son los siguientes:
- Solicitud
- Número total de buffers en proceso
- Tamaño de cada búfer, en fotogramas
- Latencia adicional después del procesador de la aplicación, como por ejemplo de un DSP
Por más precisa que sea la lista anterior de contribuyentes, también es engañosa. La razón es que el número y el tamaño del búfer son más un efecto que una causa . Lo que suele suceder es que se implementa y prueba un esquema de búfer determinado, pero durante la prueba, un audio insuficiente o excesivo se escucha como un "clic" o "pop". Para compensar, el diseñador del sistema aumenta el tamaño del búfer o el número de búfer. Esto tiene el resultado deseado de eliminar las insuficiencias o los excesos, pero también tiene el efecto secundario no deseado de aumentar la latencia. Para obtener más información sobre los tamaños de búfer, consulte el vídeo Latencia de audio: tamaños de búfer .
Un mejor enfoque es comprender las causas de los incumplimientos y los excesos, y luego corregirlos. Esto elimina los artefactos audibles y puede permitir búferes aún más pequeños o menos y así reducir la latencia.
Según nuestra experiencia, las causas más comunes de insuficiencia y exceso incluyen:
- Linux CFS (Programador completamente justo)
- subprocesos de alta prioridad con programación SCHED_FIFO
- inversión de prioridad
- latencia de programación larga
- manejadores de interrupciones de larga duración
- largo tiempo de desactivación de interrupción
- gestión de energía
- núcleos de seguridad
Programación Linux CFS y SCHED_FIFO
Linux CFS está diseñado para ser justo con las cargas de trabajo competitivas que comparten un recurso de CPU común. Esta equidad está representada por un parámetro agradable por subproceso. El valor agradable varía de -19 (menos agradable o la mayor cantidad de tiempo de CPU asignado) a 20 (el mejor o menor tiempo de CPU asignado). En general, todos los subprocesos con un valor nice determinado reciben aproximadamente el mismo tiempo de CPU y los subprocesos con un valor nice numéricamente más bajo deberían esperar recibir más tiempo de CPU. Sin embargo, el CFS es "justo" sólo durante períodos de observación relativamente largos. Durante ventanas de observación a corto plazo, CFS puede asignar el recurso de CPU de formas inesperadas. Por ejemplo, puede quitar la CPU de un subproceso con una amabilidad numéricamente baja y trasladarla a un subproceso con una amabilidad numéricamente alta. En el caso del audio, esto puede resultar en un desbordamiento o insuficiencia de datos.
La solución obvia es evitar CFS para subprocesos de audio de alto rendimiento. A partir de Android 4.1, dichos subprocesos ahora utilizan la política de programación SCHED_FIFO
en lugar de la política de programación SCHED_NORMAL
(también llamada SCHED_OTHER
) implementada por CFS.
Prioridades SCHED_FIFO
Aunque los subprocesos de audio de alto rendimiento ahora usan SCHED_FIFO
, todavía son susceptibles a otros subprocesos SCHED_FIFO
de mayor prioridad. Por lo general, estos son subprocesos de trabajo del kernel, pero también puede haber algunos subprocesos de usuario que no sean de audio con la política SCHED_FIFO
. Las prioridades SCHED_FIFO
disponibles varían de 1 a 99. Los subprocesos de audio se ejecutan con prioridad 2 o 3. Esto deja la prioridad 1 disponible para subprocesos de menor prioridad y las prioridades de 4 a 99 para subprocesos de mayor prioridad. Le recomendamos utilizar la prioridad 1 siempre que sea posible y reservar las prioridades 4 a 99 para aquellos subprocesos que se garantiza que se completarán dentro de un período de tiempo limitado, se ejecutarán con un período más corto que el período de los subprocesos de audio y se sabe que no interfieren con la programación. de hilos de audio.
Programación de tarifas monótonas
Para obtener más información sobre la teoría de la asignación de prioridades fijas, consulte el artículo de Wikipedia Programación monotónica de tasas (RMS). Un punto clave es que las prioridades fijas deben asignarse estrictamente en función del período, asignando prioridades más altas a subprocesos de períodos más cortos, no en función de la "importancia" percibida. Los subprocesos no periódicos se pueden modelar como subprocesos periódicos, utilizando la frecuencia máxima de ejecución y el cálculo máximo por ejecución. Si un subproceso no periódico no se puede modelar como un subproceso periódico (por ejemplo, podría ejecutarse con una frecuencia ilimitada o un cálculo ilimitado por ejecución), entonces no se le debe asignar una prioridad fija, ya que eso sería incompatible con la programación de subprocesos periódicos verdaderos. .
Inversión de prioridad
La inversión de prioridad es un modo de falla clásico de los sistemas en tiempo real, donde una tarea de mayor prioridad se bloquea durante un tiempo ilimitado esperando que una tarea de menor prioridad libere un recurso como (estado compartido protegido por) un mutex . Consulte el artículo " Evitar la inversión de prioridad " para conocer técnicas para mitigarla.
Latencia de programación
La latencia de programación es el tiempo entre el momento en que un subproceso está listo para ejecutarse y el momento en que se completa el cambio de contexto resultante para que el subproceso realmente se ejecute en una CPU. Cuanto más corta sea la latencia, mejor, y cualquier valor superior a dos milisegundos causa problemas de audio. Es más probable que se produzca una latencia de programación prolongada durante las transiciones de modo, como encender o apagar una CPU, cambiar entre un kernel de seguridad y el kernel normal, cambiar del modo de máxima potencia al modo de bajo consumo o ajustar la frecuencia y el voltaje del reloj de la CPU. .
Interrumpe
En muchos diseños, la CPU 0 atiende todas las interrupciones externas. Por lo tanto, un controlador de interrupciones de larga duración puede retrasar otras interrupciones, en particular las interrupciones de finalización del acceso directo a la memoria (DMA) de audio. Diseñe controladores de interrupciones para finalizar rápidamente y posponer el trabajo prolongado a un subproceso (preferiblemente un subproceso CFS o un subproceso SCHED_FIFO
de prioridad 1).
De manera equivalente, deshabilitar las interrupciones en la CPU 0 durante un período prolongado tiene el mismo resultado de retrasar la atención de las interrupciones de audio. Los tiempos de desactivación de interrupciones prolongados suelen ocurrir mientras se espera un bloqueo de giro del kernel. Revise estos bloqueos de giro para asegurarse de que estén delimitados.
Energía, rendimiento y gestión térmica.
La administración de energía es un término amplio que abarca esfuerzos para monitorear y reducir el consumo de energía mientras se optimiza el rendimiento. La gestión térmica y la refrigeración de ordenadores son similares pero buscan medir y controlar el calor para evitar daños por exceso de calor. En el kernel de Linux, el gobernador de la CPU es responsable de la política de bajo nivel, mientras que el modo de usuario configura la política de alto nivel. Las técnicas utilizadas incluyen:
- escalado de voltaje dinámico
- escalado dinámico de frecuencia
- habilitación de núcleo dinámico
- conmutación de clúster
- puerta eléctrica
- conexión en caliente (intercambio en caliente)
- varios modos de suspensión (detener, detener, inactivo, suspender, etc.)
- proceso de migración
- afinidad del procesador
Algunas operaciones de gestión pueden dar lugar a "paros laborales" o momentos durante los cuales el procesador de la aplicación no realiza ningún trabajo útil. Estos paros laborales pueden interferir con el audio, por lo que dicha gestión debe diseñarse para un paro laboral aceptable en el peor de los casos mientras el audio esté activo. Por supuesto, cuando la fuga térmica es inminente, ¡evitar daños permanentes es más importante que el audio!
Núcleos de seguridad
Un núcleo de seguridad para la gestión de derechos digitales (DRM) puede ejecutarse en los mismos núcleos de procesador de aplicaciones que los utilizados para el núcleo del sistema operativo principal y el código de la aplicación. Cualquier momento durante el cual una operación del núcleo de seguridad esté activa en un núcleo es efectivamente una interrupción del trabajo ordinario que normalmente se ejecutaría en ese núcleo. En particular, esto puede incluir trabajos de audio. Por su naturaleza, el comportamiento interno de un núcleo de seguridad es inescrutable desde las capas de nivel superior y, por lo tanto, cualquier anomalía de rendimiento causada por un núcleo de seguridad es especialmente perniciosa. Por ejemplo, las operaciones del núcleo de seguridad no suelen aparecer en los seguimientos de cambio de contexto. A esto lo llamamos "tiempo oscuro": tiempo que transcurre pero que aún no se puede observar. Los núcleos de seguridad deben diseñarse para una interrupción del trabajo aceptable en el peor de los casos mientras el audio esté activo.