在最近的工作中涉及到调整CPU 频率,当负载降低后,通过call相应接口降低CPU的运行频率,来达到节能的目的。通过turbostat和cpupower monitor查看CPU实际频率并没有降低,但是/proc/cpuinfo中的”cpu MHz“和/sys/devices/system/cpu/cpufreq/policyX/scaling_cur_freq确实预期的频率值。两者之间是矛盾的,所以花了些时间来看看,为什么会发生这种现象。
CPU的几种频率
1. 基准频率
非turbo的频率,可通过/proc/cpuinfo中的”model name“ 字段查到,或者通过读取两次TSC的值来得到基准频率。
题外话:在Intel较新的CPU中,实现了TSC不会随CPU频率/节能状态变化而变化,它采用固定频率,在初始化时就已经设定好。
2. 运行频率
CPU实际运行频率,通过IA32_APERF和IA32_MPERF这两个寄存器的值来计算CPU freq,公式如下:
MHz = base_MHz * delta_APERF / delta_MPERF base_MHz:基准频率 delta_APERF:一定时间间隔内实际消耗cycle的个数,排除idle state的cycle delta_MPERF:一定时间间隔内按照基准频率走过的cycle个数
操作系统会周期(100ms)计算CPU频率,进而调整CPU频率。
通过turbostat和cpupower monitor可查看到CPU的实际频率。
3. Turbo boost
Intel睿频加速,可以通过提高CPU频率(官方超频)来满足算力需求。具体频率还受到温度,功率等限制。在不超过TDP的前提下,尽可能挖掘CPU潜力。
这里的文章介绍了,为什么在高负载情况下,CPU不能达到最大睿频,因为温度,功耗等方面的限制,不能使所有CPU全部达到最大睿频,硬件会自动选择当前合适的超频频率。
Linux CPUFreq
参看Blog:Linux cpufreq framework | 田宇的个人博客
初探Linux CPU动态调频与实测 - 知乎
这里补充下底层驱动,目前在Intel平台有两种driver:acpi-cpufreq和intel_pstate
当在grub中添加intel_pstate=disable,OS会load acpi-cpufreq,它所提供的调度策略较丰富,具体看上面的blog。
intel_pstate 是Intel电源管理大类中的一部分,看到的资料显示它会提供3种mode:
Active mode with HWP
Active mode without HWP
Passive mode
具体的governor还是 Performance and Powersave。
intel_pstate CPU Performance Scaling Driver — The Linux Kernel documentation
P 状态全称为Device and Processor Performance State,P 状态是以控制电量消耗来降低设备或CPU 的性能,对 D0 状态的设备、C0 状态的CPU 进行了更细致的划分。
P0 通常的模式。以最高性能、最大耗电量运行。
P1 运行在低于最高性能、最大耗电量的模式
Pn n 的值越大,性能和耗电量越低。
将scaling_governors设置成ondemand:根据当前负载动态调整频率。跳到最高频率,然后可能随着空闲时间的增加而退回
uncore频率
除了CPU核主频外,还有外部辅助电路,也就是uncore(包括但不限于MMU、PCI ctrl等)当负载较低时,也可以通过调整uncore频率进一步节能
AMD机器:使用amd-df-util
节能模式:--fabric-pstate 2 --min-link-width 2 --max-link-width 8
性能模式:--fabric-pstate 0 --min-link-width 16 --max-link-width 16
Intel机器:
设置uncore: wrmsr 0x620 1418
(14最低频率,18最高频率。14就是1.6+0.4=2.0GHz),根据负载uncore动态调整
查询当前uncore: rdmsr 0x621 --
查询设置的uncore:rdmsr 0x620
查询所有CPU的uncore:rdmsr -a 0x620
一些问题
1. 为什么/proc/cpuinfo 和 cpupower -c X frequency-info 显示的不是实际运行频率?
当手动设置CPU频率时:cpupower -c X frequency-set -f 2000000
流程:cmd_freq_set -> do_one_cpu -> cpufreq_set_frequency -> [WRITE_SCALING_SET_SPEED] = "scaling_setspeed" -> 写MSR 0x199
同时这个request freq 也会同步到 /proc/cpuinfo、SCALING_CUR_FREQ 、CPUINFO_CUR_FREQ
而cpupower -c X frequency-info:直接去读取 scaling_cur_freq 具体函数cpufreq_get_freq_kernel
2. 为什么手动设置CPU运行频率但是没有生效?
当enable HT后,一个物理core模拟出两个逻辑core,逻辑CPU间共享执行单元。从硬件上来说就不支持两个逻辑CPU运行在不同的频率。
从Intel SDM也可看出,target freq可以按逻辑cpu设置,但是否调整,硬件会判断是否两个逻辑CPU都设置了0x199,当两者都设置时,取其中的最大值。
实验结果:
只更改一个逻辑CPU(22)目标频率, 22/70 是一个物理core模拟出的两个逻辑CPU
turbostat显示,频率并未改变。
设置cpu-70的目标频率为1.7GHz
turbostat显示频率为2.0GHz
3. 为什么最初target freq是3.9GHz,但实际运行频率为3.1GHz?
此时cpufreq policy是performance,会维持在最大频率运行。但是睿频受到多方面因素的影响,整体的TDP,温度等,所以不可能长时间维持在最高频率上运行,逐步降档,直到满足功耗、温度的限制后,稳定在了3.1GHz。
结论
1. 查询CPU运行频率的正确姿势:
1)cpupower monitor
2)turbostat
3)rdmsr --bitfield 15:8 -d 0x198 -p X
2. 设置CPU运行频率的方法:
cpupower -c X frequency-set -f X
它同时把governor改为userspace (如果之前不是),设置完同一个core上的两个逻辑CPU才会生效。