目录
概要
本文描述了可以减少调度时钟中断的次数的Kconfig选项和引导参数,从而来提高效率,减少系统抖动。减少系统抖动对于某些计算密集型高性能计算(HPC)程序和实时程序非常重要。
下面描述三种方法来管理:调度时钟中断(schedule-clock interrupts),也称为“调度时钟滴答”(scheduling-clock ticks)或简称“滴答”(ticks):
- 永不忽略调度时钟滴答(对于旧内核,CONFIG_HZ_PERIODIC=y或CONFIG_NO_HZ=n),一般不使用这个选项。
- 只忽略空闲cpu上的调度时钟滴答(对于旧内核,CONFIG_NO_HZ_IDLE=y或CONFIG_NO_HZ=y)。这是最常用的方法,也是默认方法。
- 忽略空闲cpu或只有一个可运行任务CPU的调度时钟滴答(CONFIG_NO_HZ_FULL=y)。除非正在运行实时应用程序或某些类型的HPC任务,否则通常不希望使用此选项。
下面的三个部分将描述这三种情况,然后是关于特定RCU的注意事项,第四部分讨论测试,第五列出已知问题。
永远不要忽略调度时钟滴答
NEVER OMIT SCHEDULING-CLOCK TICKS
---------------------------------
20世纪90年代和21世纪初的老版本Linux不能忽略调度时钟滴答。事实证明,有些情况下,这种老式的方法仍然是正确的方法,例如,在繁重的工作任务时,有大量的负载,这些负载使用的CPU时间较短,这就导致有非常频繁的空闲时间,但这些空闲的周期也很短(几十或几百个微秒)。对于这种类型的工作负载,通常会以任何方式提供调度时钟中断,因为每个CPU经常会有多个可运行的任务。 在这些情况下,除了增加切换到空闲和从用户和内核执行之间转换的开销之外,尝试关闭调度时钟中断没有任何效果。
可以使用CONFIG_HZ_PERIODIC = y(或旧内核的CONFIG_NO_HZ = n)选择此操作模式。
但是,如果正在运行具有较长空闲时段的轻型工作负载,频繁的调度时钟中断会导致功耗过大。该问题在电池供电的设备上会尤其突出,它会导致电池使用时间极短。如果需要运行轻量级工作负载,则应阅读以下部分。
此外,如果你运行实时工作任务或具有较少交互的HPC任务,调度时钟中断会降低应用性能。如果这同你的情形一致,则应阅读以下两节。
为空闲cpu的省略调度时钟滴答
OMIT SCHEDULING-CLOCK TICKS FOR IDLE CPUs
-----------------------------------------
向空闲CPU发送调度时钟中断几乎没有意义。 因为调度时钟中断的主要目的是强制繁忙的CPU关注到其它任务,而空闲的CPU则没有此需求。
Kconfig的CONFIG_NO_HZ_IDLE = y 选项避免内核向空闲CPU发送调度时钟中断,这对电池供电的设备和高度虚拟化的大型机都至关重要。 运行CONFIG_HZ_PERIODIC = y内核的电池供电设备会非常快速地耗尽电池电量,一般比运行CONFIG_NO_HZ_IDLE = y内核的同一设备快2-3倍。 运行1,500个OS实例的主机可能会发现其一半的CPU时间被不必要的调度时钟中断所消耗。 在这些情况下,就有强烈的需求避免向空闲CPU发送调度时钟中断。当然,dyntick-idle模式也是有代价的:
1.它增加了进出空闲循环的路径上执行的指令数。
2.在许多架构中,dyntick-idle模式也增加了代价较高的时钟重编程操作的数量。
因此,对实时响应要求较高的系统通常运行配置为:CONFIG_HZ_PERIODIC = y的内核(或老版本的CONFIG_NO_HZ = n),以避免降低空闲转换延迟。
未接收调度时钟中断的空闲CPU被称为“dyntick-idle”,“dyntick-idle mode”,“in nohz mode”或“running tickless”。 本文档的其余部分将使用“dyntick-idle mode”来表示。
有一个引导参数“nohz=”,可用于通过配置“nohz = off”,开指定配置了CONFIG_NO_HZ_IDLE = y内核中的dyntick-idle模式。 默认情况下,CONFIG_NO_HZ_IDLE = y内核是以“nohz=on”启动,dyntick-idle模式使能。
省略只有一个可运行任务的CPU的调度时钟功能
OMIT SCHEDULING-CLOCK TICKS FOR CPUs WITH ONLY ONE RUNNABLE TASK
----------------------------------------------------------------
如果一个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()等系统调用返回自适应时的准确值(这不是CONFIG_NO_HZ_IDLE = y的问题,因为没有正在运行的用户进程来观察时钟频率的轻微漂移。)因此,禁止启动CPU进入自适应滴答模式。 指定包含引导CPU的“nohz_full =”掩码将导致引导时错误消息,并且将从掩码中删除引导CPU。 请注意,这意味着您的系统必须至少有两个CPU,以便CONFIG_NO_HZ_FULL = y为您执行任何操作。
或者,CONFIG_NO_HZ_FULL_ALL = y Kconfig参数指定除引导CPU之外的所有CPU都是自适应滴答CPU。 “nohz_full =”引导参数将覆盖此Kconfig参数,因此如果同时指定了CONFIG_NO_HZ_FULL_ALL = y Kconfig参数和“nohz_full = 1”引导参数,则引导参数将生效,以便只有CPU 1将是 自适应滴答CPU。
最后,自适应滴答CPU必须卸载其RCU回调。 这将在下面的“RCU影响”部分中介绍。
通常,CPU尽可能长时间保持自适应滴答模式。 特别是,转换到内核模式不会自动更改模式。 相反,CPU将仅在需要时退出自适应滴答模式,如,如果该CPU将RCU回调入队。
就像dyntick-idle模式一样,自适应滴答模式的好处页不是免费的:
- CONFIG_NO_HZ_FULL选择CONFIG_NO_HZ_COMMON,因此如果不运行dyntick idle,则无法运行自适应滴答。 这种依赖性延伸到实现中,因此CONFIG_NO_HZ_IDL的所有成本也由CONFIG_NO_HZ_FULL引起。
- 由于需要通知内核子系统(例如RCU)模式的变化,用户/内核转换稍微昂贵一些。
- 自适应滴答CPU上的POSIX CPU定时器可能会错过最后期限(可能无限期),因为它们目前依赖调度 - 滴答中断。 这可以通过以下两种方式之一修复:(1)防止带POSIX CPU计时器的CPU进入自适应滴答模式,或(2)使用hrtimers或其他自适应滴答免疫机制使POSIX CPU计时器正常启动。
- 如果有更多的perf事件等待超过硬件可以容纳,它们通常是轮询的,以便在一段时间内收集所有的数据。adaption -tick模式可能会阻止这种轮询。这可能会通过阻止挂起大量perf事件的cpu进入adaptive-tick模式来修复。
- 自适应滴答CPU的调度程序统计信息的计算结果可能与非自适应滴答CPU的计划程序略有不同。 这可能反过来扰乱实时任务的负载平衡。
- 自适应滴答禁用LB_BIAS调度程序功能。
尽管预计会随着时间的推移进行改进,但自适应滴答对于许多类型的实时和计算密集型应用程序非常有用。 但是,上面列出的缺点意味着默认情况下不应该(尚未)启用自适应滴答。
RCU的影响
RCU IMPLICATIONS
----------------
在某些情况下,不允许空闲CPU进入dyntick-idle模式或自适应滴答模式,最常见的情况是该CPU有RCU回调挂起。
CONFIG_RCU_FAST_NO_HZ = y Kconfig选项可用于使这些CPU无论如何都进入dyntick-idle模式或自适应滴答模式。 在这种情况下,计时器将每四个jiff唤醒这些CPU,以确保及时处理RCU回调。
另一种方法是使用CONFIG_RCU_NOCB_CPU = y Kconfig选项将RCU回调处理卸载到“rcuo”kthreads。 可以通过以下几种方法选择要卸载的特定CPU:
1.三个互斥的Kconfig选项之一指定CPU卸载的构建时默认值:
a. CONFIG_RCU_NOCB_CPU_NONE = y Kconfig选项导致没有CPU被卸载。
b. CONFIG_RCU_NOCB_CPU_ZERO = y Kconfig选项导致CPU 0被卸载。
c. CONFIG_RCU_NOCB_CPU_ALL = y Kconfig选项会导致所有CPU被卸载。 请注意,回调将被卸载到“rcuo”kthreads,并且这些kthreads实际上将在某些CPU上运行。 但是,这种方法可以精确控制回调运行的CPU以及它们的调度优先级(包括默认的SCHED_OTHER),并且它还允许在运行时动态地改变该控制。
2.“rcu_nocbs =”内核引导参数,以逗号分隔的CPU和CPU范围列表为例,“1,3-5”选择CPU 1,3,4和5.指定的CPU将被卸载 除了由CONFIG_RCU_NOCB_CPU_ZERO = y或CONFIG_RCU_NOCB_CPU_ALL = y指定为卸载的任何CPU之外。 这意味着“rcu_nocbs =”引导参数对使用RCU_NOCB_CPU_ALL = y构建的内核没有影响。
卸载的CPU永远不会对RCU回调进行排队,因此RCU永远不会阻止卸载的CPU进入dyntick-idle模式或自适应滴答模式。 也就是说,请注意,如果需要,可以由用户空间将“rcuo”kthreads固定到特定的CPU。 否则,调度程序将决定运行它们的位置,这可能是也可能不是您希望它们运行的位置。
测试
TESTING
-------
因此,您启用了本文档中描述的所有操作系统抖动功能,但未发现工作负载行为的任何变化。 这是因为您的工作负载不受操作系统抖动的影响那么大,还是因为其他东西阻碍了? 本节通过提供一个简单的OS-jitter测试套件来帮助回答这个问题,该套件可在以下git存档的分支主机上获得:
git://git.kernel.org/pub/scm/linux/kernel/git/frederic/dynticks-testing.git
克隆此存档并按照README文件中的说明进行操作。 此测试过程将生成一条跟踪,使您可以评估是否已成功从系统中删除OS抖动。 如果此跟踪显示您已尽可能多地删除了OS抖动,那么您可以得出结论,您的工作负载对操作系统抖动并不敏感。
注意:此测试要求您的系统至少有两个CPU。 我们目前没有一种从单CPU系统中删除OS抖动的好方法。
已知的问题
KNOWN ISSUES
------------
1. Dyntick-idle会稍微减慢与空闲的转换。在实践中,除了最激进的实时工作负载之外,这不是一个问题,它可以选择禁用dyntick-idle模式,这是大多数人都采用的选项。但是,一些工作负载无疑会希望使用自适应滴答来消除调度时钟中断延迟。以下是这些工作负载的一些选项:
a.使用来自用户空间的PMQOS通知内核您的延迟要求(首选)。
b.在x86系统上,使用“idle = mwait”引导参数。
c.在x86系统上,使用“intel_idle.max_cstate =”来限制最大C-state深度。
d.在x86系统上,使用“idle = poll”引导参数。但请注意,使用此参数可能会导致CPU过热,这可能会导致热量限制降低延迟 - 并且这种降级甚至可能比dyntick-idle更糟。此外,此参数可有效禁用Intel CPU上的Turbo模式,这可显着降低最高性能。
2. Adaptive-ticks会略微降低用户/内核的转换速度。 对于计算密集型工作负载而言,预计这不会成为问题,因为这些工作负载几乎没有这样的转换。 需要进行仔细的基准测试,以确定其他工作负载是否会受到此影响的显着影响。
3. Adaptive-ticks不执行任何操作,除非给定CPU只有一个可运行的任务,即使有许多其他情况需要不需要调度时钟。举一个例子,考虑一个具有一个可运行的高优先级SCHED_FIFO任务和任意数量的低优先级SCHED_OTHER任务的CPU。在这种情况下,CPU需要运行SCHED_FIFO任务,直到它阻塞或某个其他更高优先级的任务唤醒(或被分配给)该CPU,因此向该CPU发送调度时钟中断没有意义。 但是,当前的实现仍然向具有单个可运行SCHED_FIFO任务和多个可运行SCHED_OTHER任务的CPU发送调度时钟中断,即使这些中断是不必要的。
即使在给定的CPU上有多个可运行的任务,在当前运行任务的时间片到期之前中断该CPU几乎没有意义,这几乎总是比下一个调度时钟中断的时间长。
更好地处理这些情况是未来的工作。
4. 需要重新启动才能重新配置自适应空闲和RCU回调卸载。 如果需要,可以提供运行时重新配置,但是,由于在运行时重新配置RCU的复杂性,需要有一个非常好的理由。 特别是考虑到您可以直接从所有CPU卸载RCU回调并将它们固定在您希望它们固定的位置。
5. 需要额外的配置来处理其他OS抖动源,包括中断和系统实用程序任务和进程。 此配置通常涉及将中断和任务绑定到特定CPU。
6. 目前只能通过限制工作负载来消除某些OS抖动源。 例如,消除由于全局TLB击落引起的OS抖动的唯一方法是避免导致这些击落的解映射操作(例如内核模块卸载操作)。 再举一个例子,通过使用大页面和约束应用程序使用的内存量,可以减少页面错误和TLB未命中(在某些情况下可以消除)。 预处理工作集也很有帮助,特别是当与mlock()和mlockall()系统调用结合使用时。
7. 除非所有CPU都处于空闲状态,否则至少有一个CPU必须保持调度时钟中断,以支持准确的计时。
8. 如果可能存在一些自适应滴答CPU,则至少有一个CPU保持调度时钟中断,即使所有CPU都处于空闲状态。
更好地处理这种情况正在进行中。
9. 某些流程处理操作仍然需要偶尔的调度时钟滴答。 这些操作包括计算CPU负载,维护sched平均值,计算CFS实体vruntime,计算avenrun以及执行负载平衡。 它们目前通过每隔一秒左右的调度时钟滴答来满足。 即使对于这些不频繁的调度时钟滴答,正在进行的工作也将消除这种需求。