音频延迟的贡献者

本页重点介绍输出延迟的贡献因素,但类似的讨论也适用于输入延迟。

假设模拟电路没有显著贡献,那么音频延迟的主要表面因素如下:

  • 应用
  • 管道中的缓冲区总数
  • 每个缓冲区的大小(以帧为单位)
  • 应用处理器之后的额外延迟,例如来自 DSP 的延迟

尽管上述贡献者列表可能很准确,但也具有误导性。原因是缓冲区计数和缓冲区大小更多的是结果,而不是原因。通常发生的情况是,先实施和测试给定的缓冲区方案,但在测试期间,会听到音频欠载或过载,表现为“咔哒”声或“砰”声。为了补偿,系统设计人员随后会增加缓冲区大小或缓冲区计数。这达到了消除欠载或过载的预期效果,但也产生了增加延迟的意外副作用。有关缓冲区大小的更多信息,请参阅视频 音频延迟:缓冲区大小

更好的方法是了解欠载和过载的原因,然后纠正这些原因。这样可以消除可听见的伪像,甚至可以允许更小或更少的缓冲区,从而减少延迟。

根据我们的经验,欠载和过载的最常见原因包括:

  • Linux CFS(完全公平调度器)
  • 具有 SCHED_FIFO 调度的高优先级线程
  • 优先级反转
  • 长调度延迟
  • 长时间运行的中断处理程序
  • 长中断禁用时间
  • 电源管理
  • 安全内核

Linux CFS 和 SCHED_FIFO 调度

Linux CFS 旨在公平对待共享通用 CPU 资源的竞争工作负载。这种公平性由每个线程的 nice 参数表示。nice 值范围从 -19(最不友善,或分配最多的 CPU 时间)到 20(最友善,或分配最少的 CPU 时间)。一般来说,具有给定 nice 值的所有线程都接收大致相等的 CPU 时间,并且 nice 值数值较低的线程应期望接收更多的 CPU 时间。但是,CFS 的“公平”仅在相对较长的观察期内有效。在短期观察窗口中,CFS 可能会以意外的方式分配 CPU 资源。例如,它可能会将 CPU 从 nice 值数值较低的线程转移到 nice 值数值较高的线程。在音频的情况下,这可能会导致欠载或过载。

显而易见的解决方案是避免对高性能音频线程使用 CFS。从 Android 4.1 开始,此类线程现在使用 SCHED_FIFO 调度策略,而不是 CFS 实现的 SCHED_NORMAL(也称为 SCHED_OTHER)调度策略。

SCHED_FIFO 优先级

尽管高性能音频线程现在使用 SCHED_FIFO,但它们仍然容易受到其他更高优先级 SCHED_FIFO 线程的影响。这些线程通常是内核工作线程,但也可能有一些策略为 SCHED_FIFO 的非音频用户线程。可用的 SCHED_FIFO 优先级范围为 1 到 99。音频线程以优先级 2 或 3 运行。这为较低优先级的线程留出了优先级 1,为较高优先级的线程留出了优先级 4 到 99。我们建议您尽可能使用优先级 1,并将优先级 4 到 99 保留给那些保证在有界时间内完成、执行周期短于音频线程周期并且已知不会干扰音频线程调度的线程。

速率单调调度

有关固定优先级分配理论的更多信息,请参阅 Wikipedia 文章 速率单调调度 (RMS)。一个关键点是,固定优先级应严格根据周期进行分配,较高优先级分配给周期较短的线程,而不是基于感知的“重要性”。非周期性线程可以建模为周期性线程,使用最大执行频率和每次执行的最大计算量。如果非周期性线程无法建模为周期性线程(例如,它可能以无界频率或无界计算量执行),则不应为其分配固定优先级,因为这与真实周期性线程的调度不兼容。

优先级反转

优先级反转 是实时系统的经典故障模式,其中较高优先级的任务因等待较低优先级的任务释放资源(例如受 互斥锁 保护的共享状态)而被无限期地阻塞。有关缓解优先反转的技术,请参阅文章“避免优先级反转”。

调度延迟

调度延迟是指线程变为可运行状态到完成结果上下文切换(以便线程实际在 CPU 上运行)之间的时间。延迟越短越好,任何超过两毫秒的延迟都会给音频带来问题。长调度延迟最有可能发生在模式转换期间,例如启动或关闭 CPU、在安全内核和正常内核之间切换、从全功率模式切换到低功率模式,或调整 CPU 时钟频率和电压。

中断

在许多设计中,CPU 0 服务于所有外部中断。因此,长时间运行的中断处理程序可能会延迟其他中断,特别是音频直接内存访问 (DMA) 完成中断。设计中断处理程序以快速完成,并将耗时的工作推迟到线程(最好是 CFS 线程或优先级为 1 的 SCHED_FIFO 线程)。

同样,长时间禁用 CPU 0 上的中断也会产生延迟音频中断服务的结果。长中断禁用时间通常发生在等待内核自旋锁时。检查这些自旋锁以确保它们是有界的。

电源、性能和散热管理

电源管理 是一个广泛的术语,涵盖了在优化性能的同时监视和降低功耗的努力。散热管理计算机冷却 类似,但旨在测量和控制热量,以避免因过热造成的损坏。在 Linux 内核中,CPU 调速器 负责低级策略,而用户模式配置高级策略。使用的技术包括:

  • 动态电压调节
  • 动态频率调节
  • 动态核心启用
  • 集群切换
  • 电源门控
  • 热插拔(热交换)
  • 各种睡眠模式(暂停、停止、空闲、挂起等)
  • 进程迁移
  • 处理器亲和性

一些管理操作可能会导致“工作停止”,或者应用程序处理器在此期间不执行任何有用工作的时间。这些工作停止可能会干扰音频,因此应将此类管理设计为在音频活动期间可接受的最坏情况工作停止。当然,当热失控迫在眉睫时,避免永久性损坏比音频更重要!

安全内核

用于 数字版权管理 (DRM) 的 安全内核 可能与用于主操作系统内核和应用程序代码的内核在相同的应用程序处理器核心上运行。安全内核操作在核心上处于活动状态的任何时间实际上都是对通常在该核心上运行的普通工作的停止。特别是,这可能包括音频工作。从本质上讲,安全内核的内部行为对于更高级别层来说是不可理解的,因此由安全内核引起的任何性能异常都尤其有害。例如,安全内核操作通常不会出现在上下文切换跟踪中。我们称之为“黑暗时间”——时间流逝但无法观察到。安全内核应设计为在音频活动期间可接受的最坏情况工作停止。