NO_HZ: 降低调度时钟中断 【ChatGPT】

发布时间 2023-12-11 22:15:49作者: 摩斯电码

降低调度时钟中断

本文描述了 Kconfig 选项和引导参数,可以减少调度时钟中断的次数,从而提高能效并减少操作系统的抖动。减少操作系统的抖动对于某些类型的计算密集型高性能计算(HPC)应用程序和实时应用程序非常重要。

有三种主要的管理调度时钟中断(也称为“调度时钟滴答”或简称“滴答”)的方式:

  1. 从不省略调度时钟中断(CONFIG_HZ_PERIODIC=y 或对于旧内核为 CONFIG_NO_HZ=n)。通常情况下,您不会选择此选项。

  2. 在空闲 CPU 上省略调度时钟中断(CONFIG_NO_HZ_IDLE=y 或对于旧内核为 CONFIG_NO_HZ=y)。这是最常见的方法,也应该是默认选项。

  3. 在只有一个可运行任务的 CPU 上省略调度时钟中断(CONFIG_NO_HZ_FULL=y)。除非您正在运行实时应用程序或某些类型的 HPC 工作负载,否则通常不会选择此选项。

接下来的三节将分别描述这三种情况,然后是关于 RCU 的特殊考虑的第四节,讨论测试的第五节,以及列出已知问题的第五节。

从不省略调度时钟中断

20世纪90年代和21世纪初期的非常旧版本的 Linux 无法省略调度时钟中断。事实证明,在某些情况下,这种老式方法仍然是正确的方法,例如,在具有大量使用短暂 CPU 闲置期的任务的重型工作负载中,在这些情况下,尝试关闭调度时钟中断将不会产生任何效果,只会增加从空闲状态切换到和从空闲状态切换到用户和内核执行之间的开销。

可以使用 CONFIG_HZ_PERIODIC=y(或对于旧内核为 CONFIG_NO_HZ=n)选择此操作模式。

然而,如果您运行的是轻负载且有长时间空闲期的工作负载,不省略调度时钟中断将导致过多的能耗。这在使用电池供电的设备上尤为糟糕,会导致极短的电池寿命。因此,如果您运行轻负载,应阅读以下部分。

此外,如果您运行的是实时工作负载或具有短迭代的 HPC 工作负载,调度时钟中断可能会降低应用程序的性能。如果您的工作负载符合这种情况,您应该阅读以下两节。

对空闲 CPU 省略调度时钟中断

如果 CPU 空闲,发送调度时钟中断就没有太大意义。毕竟,调度时钟中断的主要目的是强制繁忙的 CPU 在多个任务之间转换注意力,而空闲 CPU 没有需要转换注意力的任务。

不接收调度时钟中断的空闲 CPU 被称为“动态空闲”,“处于动态空闲模式”,“处于无滴答模式”或“运行无滴答”。本文其余部分将使用“动态空闲模式”。

CONFIG_NO_HZ_IDLE=y Kconfig 选项使内核避免向空闲 CPU 发送调度时钟中断,这对于使用电池供电的设备和高度虚拟化的大型机非常重要。运行 CONFIG_HZ_PERIODIC=y 内核的电池供电设备会非常快地耗尽电池,耗电速度可能是运行 CONFIG_NO_HZ_IDLE=y 内核的设备的 2-3 倍。运行 1,500 个操作系统实例的大型机可能会发现其 CPU 时间的一半被不必要的调度时钟中断所消耗。在这些情况下,有强烈的动机避免向空闲 CPU 发送调度时钟中断。不过,动态空闲模式并非没有代价:

  • 它增加了在进入和退出空闲循环时执行的指令数。
  • 在许多体系结构上,动态空闲模式还会增加昂贵的时钟重新编程操作。

因此,具有激进实时响应约束的系统通常运行 CONFIG_HZ_PERIODIC=y 内核(或对于旧内核为 CONFIG_NO_HZ=n),以避免从空闲状态转换的延迟。

还有一个引导参数 "nohz=",可以通过指定 "nohz=off" 来禁用 CONFIG_NO_HZ_IDLE=y 内核中的动态空闲模式。默认情况下,CONFIG_NO_HZ_IDLE=y 内核会以 "nohz=on" 启动,启用动态空闲模式。

对只有一个可运行任务的 CPU 省略调度时钟中断

如果一个 CPU 只有一个可运行任务,发送调度时钟中断就没有太大意义,因为没有其他任务可以切换到。请注意,对只有一个可运行任务的 CPU 省略调度时钟中断也意味着对空闲 CPU 省略调度时钟中断。

CONFIG_NO_HZ_FULL=y Kconfig 选项使内核避免向只有一个可运行任务的 CPU 发送调度时钟中断,这样的 CPU 被称为“自适应滴答 CPU”。这对于具有激进实时响应约束的应用程序非常重要,因为它允许它们通过调度时钟中断的最大持续时间来改善它们的最坏响应时间。对于计算密集型的短迭代工作负载也很重要:如果在给定迭代期间延迟了任何 CPU,那么其他所有 CPU 将被迫等待空闲,直到延迟的 CPU 完成。因此,延迟将乘以比 CPU 数少一个的数。在这些情况下,再次有强烈的动机避免发送调度时钟中断。

默认情况下,没有 CPU 会成为自适应滴答 CPU。引导参数 "nohz_full=" 指定了自适应滴答 CPU。例如,"nohz_full=1,6-8" 表示 CPU 1、6、7 和 8 将成为自适应滴答 CPU。请注意,您不得将所有 CPU 标记为自适应滴答 CPU:至少必须保留一个非自适应滴答 CPU 以处理时间跟踪任务,以确保像 gettimeofday() 这样的系统调用在自适应滴答 CPU 上返回准确的值。(对于 CONFIG_NO_HZ_IDLE=y 来说,这不是问题,因为没有运行的用户进程来观察时钟速率的轻微漂移。)因此,引导 CPU 被禁止进入自适应滴答模式。指定包括引导 CPU 的 "nohz_full=" 掩码将导致引导时的错误消息,并且引导 CPU 将从掩码中移除。请注意,这意味着您的系统必须至少有两个 CPU,以便 CONFIG_NO_HZ_FULL=y 对您产生影响。

最后,自适应滴答 CPU 必须卸载其 RCU 回调。这在下面的 "RCU IMPLICATIONS" 部分中有所涉及。

通常情况下,只要可能,CPU会保持自适应时钟模式。特别是,转换到内核模式不会自动改变模式。相反,CPU只有在需要时才会退出自适应时钟模式,例如,如果该CPU排队了一个RCU回调。

与动态空闲模式一样,自适应时钟模式的好处并非免费:

  • CONFIG_NO_HZ_FULL选择CONFIG_NO_HZ_COMMON,因此您不能仅运行自适应时钟而不运行动态空闲。这种依赖关系延伸到实现中,因此CONFIG_NO_HZ_FULL也会产生CONFIG_NO_HZ_IDLE的所有成本。
  • 用户/内核转换的开销稍微增加,因为需要通知内核子系统(如RCU)有关模式更改的信息。
  • POSIX CPU定时器会阻止CPU进入自适应时钟模式。需要根据CPU时间消耗采取行动的实时应用程序需要使用其他方法。
  • 如果待处理的性能事件超过硬件的容量,通常会进行轮询,以便随着时间的推移收集所有事件。自适应时钟模式可能会阻止这种轮询的发生。这可能会通过防止具有大量待处理性能事件的CPU进入自适应时钟模式来修复。
  • 自适应时钟CPU的调度器统计信息可能与非自适应时钟CPU的统计信息稍有不同。这可能会干扰实时任务的负载平衡。

尽管随着时间的推移预计会有改进,自适应时钟对许多类型的实时和计算密集型应用程序非常有用。然而,上述缺点意味着自适应时钟不应(尚未)默认启用。

RCU影响

存在一些情况,其中空闲CPU不能进入动态空闲模式或自适应时钟模式,最常见的情况是当该CPU有待处理的RCU回调时。

通过使用CONFIG_RCU_NOCB_CPU=y Kconfig选项将RCU回调处理卸载到“rcuo” kthreads来避免这种情况。可以使用“rcu_nocbs=”内核引导参数选择要卸载的特定CPU。该参数接受逗号分隔的CPU和CPU范围列表,例如,“1,3-5”选择CPU 1、3、4和5。请注意,“nohz_full”内核引导参数指定的CPU也会被卸载。

卸载的CPU将永远不会排队RCU回调,因此RCU永远不会阻止卸载的CPU进入动态空闲模式或自适应时钟模式。然而,请注意,如果需要,用户空间需要将“rcuo” kthreads固定到特定的CPU上。否则,调度器将决定在哪里运行它们,这可能是您希望它们运行的位置,也可能不是。

测试

因此,您启用了本文档中描述的所有OS抖动功能,但是没有看到工作负载行为的任何变化。这是因为您的工作负载对OS抖动的影响不大,还是因为其他原因阻碍了变化?本节通过提供一个简单的OS抖动测试套件来回答这个问题,该套件可在以下git存档的master分支上获得:

git://git.kernel.org/pub/scm/linux/kernel/git/frederic/dynticks-testing.git

克隆此存档并按照README文件中的说明操作。此测试过程将生成一个跟踪,以便您评估是否成功消除了系统中的OS抖动。如果此跟踪显示您已尽可能地消除了OS抖动,则可以得出结论,您的工作负载对OS抖动并不敏感。

注意:此测试要求您的系统至少有两个CPU。我们目前没有一种有效的方法来消除单CPU系统中的OS抖动。

已知问题

  • 动态空闲会稍微减慢进入和退出空闲状态的转换。实际上,除了最激进的实时工作负载外,这并不是一个问题,因为它们可以选择禁用动态空闲模式,大多数工作负载都会选择这个选项。然而,一些工作负载无疑希望使用自适应时钟来消除调度时钟中断延迟。对于这些工作负载,有以下一些选项:
    • 使用用户空间的PMQOS来通知内核您的延迟要求(首选)。
    • 在x86系统上,使用“idle=mwait”引导参数。
    • 在x86系统上,使用“intel_idle.max_cstate=”来限制最大C状态深度。
    • 在x86系统上,使用“idle=poll”引导参数。但是,请注意,使用此参数可能会导致CPU过热,这可能会导致热限制降低延迟,而这种降低可能比动态空闲更严重。此外,此参数会有效地禁用Intel CPU上的Turbo Mode,这可能会显著降低最大性能。
  • 自适应时钟会稍微减慢用户/内核转换。这对于计算密集型工作负载来说不是问题,因为这些工作负载的转换很少。需要进行仔细的基准测试,以确定其他工作负载是否受到此影响。
  • 除非给定的CPU处于空闲状态,否则至少有一个CPU必须保持调度时钟中断以支持准确的时间计算。
  • 如果可能存在一些自适应时钟的CPU,即使所有CPU都处于空闲状态,也会有至少一个CPU保持调度时钟中断。
  • 更好地处理这种情况是正在进行的工作。
  • 某些处理进程的操作仍然需要偶尔的调度时钟中断。这些操作包括计算CPU负载、维护调度平均值、计算CFS实体的虚拟运行时间、计算avenrun以及进行负载平衡。目前,每秒钟进行一次调度时钟中断来满足这些操作。正在进行的工作将消除甚至这些不频繁的调度时钟中断的需求。