上一节我们讨论了CPU效能方面提供的几种状态,其中MONITOR/MWAIT 指令也会对CPU的效能影响,可能导致性能下降。

下面我们就详细讨论下,Linux的2.6和4.18这两个版本的内核是如何使这些指令和节能状态的。

Linux对CPU效能的管理

Linux对CPU的效能管理主要是对 P-states 和 C-states 两种状态的控制,简单起见,这里只讨论x86架构下的一些细节。

P-states

P-states这个节能状态可能会有多种策略模式,最常见的是performance和powersave,这两种策略主要是对CPU的最高运行频率进行限制, performance的限制是CPU自身超频所能达到的最高频率;而powersave的限制是为节能状态所设计的最高工作频率,可能只有基本频率的一半。

通常,在BIOS里会有一个选项,让用户选择是让操作系统来控制此状态还是BIOS自己来。如果是后者,则操作系统是不能修改P-states的策略的,且通过系统命令看到的策略模式也不一定是BIOS中设置的策略模式;如果选择由操作系统来控制,可以通过系统命令来管理。

管理策略影响的是CPU的最大运行频率,下面我们来看一下linux控制P-states的状态等级和CPU的实际工作频率的一些细节。

在Linux中,CPU的每个时钟中断都会更新当前CPU的利用率,然后会用 ( 1.25 * 最大频率 * CPU 利用率 ) 这个公式求出CPU下一个频率,这里的"最大频率"就是上面策略所对应的限制频率;

建议配置:

• 若业务对响应延迟和稳定性要求比较高:建议在BIOS中禁用P-states状态;

• 一般情况下:建议在BIOS中开启此状态,策略模式设置为performance,同时开启Turbo Boost,并设置由BIOS来管理电源使用。

由于各厂商的BIOS差异可能较大,我们需要仔细看其说明,像笔者所见的华为机型的BIOS:

• P-states状态的是由谁来管理,是通过"Power Performance Tuning"这个配置项来控制;

• P-states状态是是否开启,对应的是“EIST”配置项。这里的EIST指的是intel的一项技术,Enhanced Intel SpeedStep Technology,它是P-states在Intel平台上的具体应用和实现。

C-states

C-states没有那么多策略,只有开启和关闭两种选择;开启后可以进一步配置CPU所能进入的最深状态等级,在BIOS里还可以控制是否启用Package C-states,C1E等。(C1E:一种介于C1和C2状态之间的一种状态,其全称为 Enhanced Halt State,因为CPU halt后进入的C1状态,C1E会在C1状态的基础上进一步降低电压来节能,所以相对C1状态需要更长的唤醒时间。)

通常我们都是压榨服务器的性能,所以BIOS中的C-states状态一般都是关闭的,但有时你会发现,在某些内核版本下CPU还是可以进入C3或C6这种深度的睡眠状态,导致系统性能不稳定或响应时间变长。这可能是因为BIOS里开启了前文提到的 MONITOR/MWAIT 配置项,在它的帮助描述中可能会有这样一句话:“某些操作系统会根据此项来自主开启C-states,即使你已经关闭了此状态”。

有时像上面那样一句意思并不明确,但影响又很大的提醒,真的是让人很崩溃;你可能会有和我一样的疑问:到底要是什么样的操作系统?它又是为什么会强制开启C-states?为了解决这些疑问,下面我们就详细讨论下2.6和4.18这两个linux内核版本管理C-states状态时所存在的差异。

注:由于CPU型号的差异,导致Linux可能会使用不同的处理方式;为了避免不必要的困扰,在此特意声明,下面所讨论的内容是基于Intel Xeon E5-2630 v4这个型号来说的。

kernel-2.6

Linux对C-states的管理,主要是看cpu的idle用的是哪种方式,在2.6内核中存在四种内置idle方式:poll_idle、mwait_idle、c1e_idle、default_idle,还有一种是驱动idle方式:cpuidle_idle_call;

其中内置idle方式,可以通过在启动命令行中增加"idle="来配置,比方说增加"idle=poll",会强制使用 poll_idle 方式;默认会使用default_idle,它就是简单的使用halt指令让CPU进入C1状态。

要进入驱动idle方式,需要未强制使用内置idle方式,并且满足使用当前驱动的硬件条件;

在2.6内核中有两种idle驱动:intel_idle 和 acpi_idle。

intel_idle驱动所需的硬件条件大体为:CPU制造商为intel,支持mwait指令,属于酷睿家族,微架构最高支持Crystal Well;

acpi_idle驱动所需的硬件条件大体为: 支持比C1更深的睡眠状态。可以通过cat /proc/acpi/processor/CPU*/power 来查看所支持的睡眠状态:

cpu的主要性能与cache的关系(通过存储性能问题来看CPU的效能管理)(1)

查看是否支持主要是看“states:”下面显示的内容,可以看出只有在C-states开启后,才支持C2以上的状态。(这里的C2、C3状态指的并不是CPU的C-states状态等级,后面的数字只是一个索引)

然后我们来看一下Intel Xeon E5-2630 v4这个型号CPU,它是Broadwell微架构,比Crystal Well要高,所以你会在dmesg的输出中见到“intel_idle: does not run on family 6 model 79”字样。

最终2.6的内核无论如何也不会使用intel_idle驱动,只有在开启C-states状态且没有强制使用内置idle方式,才会启用acpi_idle驱动。

只要有一个驱动被启用,当前的idle方式就会被设置为cpuidle_idle_call。它会使用当前cpuidle的governor来根据当前系统状态,选择下一个要进入的C-states状态等级。

通常cpuidle的governor为menu,其在选择时大体上是看两个因素:一是当前状态等级的目标驻留时间,因为C-states状态的切换也是一种额外的消耗,所以它会根据之前CPU的表现来预测一个可能的驻留时间,预测时间比目标时间长才会被选择;另一方面是唤醒延迟(对应上面命令输出中的latency[041]中41这个值),这个延迟会与系统的延迟请求做比较,只有比系统延迟请求小的状态等级才会被选择。

这里的系统延迟请求,默认值是2000s,一个很大的值(对应上面命令输出中的 maximum allowed latency 字样后面的值);操作系统提供了一个接口供用户来设置此值,这个接口是一个字符设备,路径为 /dev/cpu_dma_latency ,最简单的方法是安装一个名为tuned的服务,通过设置不同的profile来改变此值。

cpuidle_idle_call在拿到menu governor的下一个状态等级后,会通过acpi_idle驱动来让CPU进入指定状态;acpi_idle驱动会尝试以下三种方式:mwait指令、halt指令和inb指令。

根据之前文中所提到的,halt指令只能进入C1,mwait可以进入更深的睡眠等级。那inb指令又是什么呢?inb指令是操作系统对IO端口直接访问的一种汇编指令,具体什么型号CPU能支持此方式来进入更深的睡眠状态还不得而知,至少我们目前正在讨论的CPU型号不支持;

从中我们可以看出,若关闭 MONITOR/MWAIT 指令,即使开启C-states,系统也最多只能进入C1状态。

场景复现

上面用很长一段篇幅描述了在2.6内核下如何才能让CPU进入更深的睡眠状态,看上似乎没有什么必要,因为我们通过不考虑节能,要提高性能都是关闭节能状态;但这里我们就遇到了一个特殊情况,在上一节的一开始我们提到过,2.6内核一开始BIOS里设置的是节能模式,性能表现很好,而改成性能模式后,性能却下降了,这里我们用另外一个相似的测试命令复现一下。

使用节能模式下的执行情况和睡眠状态:

cpu的主要性能与cache的关系(通过存储性能问题来看CPU的效能管理)(2)

cpu的主要性能与cache的关系(通过存储性能问题来看CPU的效能管理)(3)

使用性能模式下的执行情况和睡眠状态:

cpu的主要性能与cache的关系(通过存储性能问题来看CPU的效能管理)(4)

cpu的主要性能与cache的关系(通过存储性能问题来看CPU的效能管理)(5)

通过上面两种模式的输出对比,我们可以看以明显的看出,节能模式的运行时间要比性能模式快20 %;其主要得益于CPU频率,3.1Ghz差不多比2.4Ghz高20% 。

我们再来看看 Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz 这个CPU型号的性能参数,其正常工作频率是2.2Ghz,最大睿频3.1Ghz;

然后再看一下CPU睡眠状态,节能模式下大部分时间是在C6状态下,而性能模式下大部分时间是在C1状态下;

我们知道C6状态节省的电力是要比C1多的,在开启Turbo Boost功能的情况下,明显节能模式能提供更多的电力来超频,其性能表现自然就要看上去好。

看到这里我们不禁要问,性能模式的意义在哪里?我们再来看一组数据对比:

场景1:多次执行命令 `perf stat echo 1`耗时

cpu的主要性能与cache的关系(通过存储性能问题来看CPU的效能管理)(6)

场景2:开启多个线程来并行执行命令`echo 2^1234567%2 | perf stat bc`的平均耗时

cpu的主要性能与cache的关系(通过存储性能问题来看CPU的效能管理)(7)

通过上面的数据我们看到,性能模式在高并发场景下的性能和稳定性方面有明显的优势,所以性能模式所擅长的场景是低响应延迟和高并发,而节能模式可能在其他场景下反而表现会更好。

kernel-4.18

在4.18内核中cpuidle_idle_call成为内置且主要的idle方式,在没有可用cpuidle驱动的情况,它会调用内置的default_idle_call方式,这种方式2.6中default_idle差不多,都是用halt指令进入C1状态。

4.18内核的cpuidle驱动有很多,像ARM、MIPS类型CPU都有了专门的驱动,这里我们直接看intel_idle驱动。

要启用intel_idle驱动所满足的硬件条件与2.6内核差不太多,都是需要开启MWAIT指令,但它支持了更多的CPU型号,我们所用的CPU也在其列。所以要启用这个驱动,只看是否启用了MONITOR/MWAIT指令,与C-states的设置无关,因为内核会维护一个固有的状态表。

与2.6内核类似,在有可用的驱动后,cpuidle_idle_call会使用menu governor来选择要进入的一个状态等级,menu governor选择方法也基本一样,只不过其中系统延迟请求的接口发生了变化,对应的路径改为 /sys/kernel/debug/pm_qos/cpu_dma_latency,其类型也变为一般的文件类型;不过依然可以使用对应版本的tuned服务来直接修改。

在选出下一个状态等级后,cpuidle_idle_call就会调用intel_idle驱动来与CPU进行交互,intel_idle只使用一种方式:MONITOR/MWAIT指令。

整体上看4.18内核并没有与2.6内核有太多不同,只是驱动换成了intel_idle,从而在BIOS中只要开启了 MONITOR/MWAIT指令 就会进入睡眠状态。

那为什么4.18内核会与2.6内核表现截然不同,在节能模式下反而比性能模式表现要差呢?

由于篇幅原因,我们将在下一节,通过复现4.18内核的场景来解答这个问题,请关注我们的后续推送。(未完 待续)


欢迎大家关注“58架构师”微信公众号,定期分享云计算、AI、区块链、大数据、搜索、推荐、存储、中间件、移动、前端、运维等方面的前沿技术和实践经验。

,