目录
之前章节介绍的电源管理都都直接下电,不用电当然能节能,但是还有比较温柔的方法就是通过调节电压频率。比如经常的一个说法:CPU太热了跑不动了,快给降频下。频率就干活的速度,干活太快,CPU都要烧了,太热了,费电啊。但是在户外的设备,环境温度过高还是要考虑遮阳、通风来应对,但降频也可以降低温度,但是会引起卡顿啊。
一般电压和频率是成对出现的,也叫OPP(Operating Performance Points),对其进行调节也叫DVFS(Dynamic Voltage and Frequency Scaling),下面就来揭开这些技术的神秘面纱。
1. 整体介绍
1.1 DVFS
DVFS(Dynamic Voltage and Frequency Scaling)即动态电压频率调整。这项技术可以根据芯片运行的应用程序的计算需求制定策略,动态调整电压和频率:
-
在不需要高性能时,降低电压和频率,以降低功耗;
-
在需要高性能时,提高电压和频率,以提高性能,从而达到兼顾性能而又节能的目的。
DVFS技术利用了CMOS芯片的特性:CMOS芯片的能量消耗正比于电压的平方和时钟频率:
-
减少能量消耗需要降低电压和频率。
-
仅仅降低时钟频率并不节约能量,因为时钟频率的降低会带来任务执行时间的增加。调节电压需要以相同的比例调节频率以满足信号传播延迟要求。然而不管是电压调节还是频率调节,都会造成系统性能的损失,并增加系统的响应延迟。
-
DVFS技术是以延长任务执行时间为代价来达到减少系统能量消耗的目的,体现了功耗与性能之间的权衡。可以通过减少时钟频率来降低通用处理器功耗的。
1.2 Linux 软件流程框图
CPUFreq系统流程:
-
用户app可以使用/sys/devices/system/cpu/cpu0/cpufreq/下的接口文件设置cpu频率
-
设置频率的时候会调用相关governor的函数,主要包括查询、设置等
-
governor负责采集与系统负载有关的信号,计算当前的系统负载。根据系统的当前负载,根据调节策略预测系统在下一时间段需要的性能。将预测的性能转换成需要的频率和电压在cpufreq table中选择一个,进行调整芯片的时钟和电压设置。
-
governor需要设置的时候会调用cpufreq core的接口cpufreq_driver->target_index进行设置
-
driver会继续调用opp驱动clk_set_rate(clk, freq)接口进行寄存器设置,让电压频率生效 另外:动态策略的governor会自动收集系统中的各种信号进行动态调节
DVFS调节策略 一味的降频降压当然是不能降低功耗的,因为低频下运行可能使系统处理任务的时长增加,从而整体上可能增加了功耗。所以DVFS的核心是动态调整的策略,其目的是根据当时的系统负载实时调整,从而提供满足当时性能要求的最低功率,也就达到了最低功耗。
需要统计出这些模块的负载情况,基本的策略当然是工作负载增加则升频升压,工作负载降低则降频降压。工作负载的粗略模型是在一个时间窗口内,统计模块工作的时间长度,设定不同阈值,高阈值对应高电压高频率,低阈值对应低电压低频率。每次统计值穿过阈值边界,触发DVFS转换。
> 在调整频率和电压时,要特别注意调整的顺序: > - 当频率由高到低调整时,应该先降频率,再降电压; > - 相反,当升高频率时,应该先升电压,再升频率。
2. 相关代码介绍
2.1 整体代码框架
内核目前有一套完整的代码支持DVFS,具体可参考内核下drivers/cpufreq/。
image.png
-
cpufreq core:是cpufreq framework的核心模块,和kernel其它framework类似,主要实现三类功能:
-
向上,以sysfs的形式向用户空间提供统一的接口,以notifier的形式向其它driver提供频率变化的通知。
-
内部,抽象调频调压的公共逻辑和接口,主要围绕struct cpufreq_driver、struct cpufreq_policy和struct cpufreq_governor三个数据结构进行。包括:围绕结构struct cpufreq_governor提供governor框架,用于实现不同的频率调整机制;围绕struct cpufreq_policy实现的一些功能等。
-
向下:提供CPU频率和电压控制的驱动框架,封装通用操作接口给驱动,方便底层驱动的开发;
-
cpufreq governor:负责调频调压的各种策略,每种governor计算频率的方式不同,根据提供的频率范围和参数(阈值等),计算合适的频率。
-
cpufreq driver:负责平台相关的调频调压机制的实现,基于cpu subsystem driver、OPP、clock driver、regulator driver等模块,提供对CPU频率和电压的控制。kernel中实现了比较通用的驱动模块cpufreq-dt.c
-
cpufreq stats:负责调频信息和各频点运行时间等统计,提供每个cpu的cpufreq有关的统计信息。
2.2 用户态接口
cpufreq相关驱动模块加载后,会在各cpu下创建:/sys/devices/system/cpu/cpuX/cpufreq接口
这是一个软链接:cpufreq -> ../cpufreq/policy0
image.png
前缀是scaling的属性文件表示软件可调节的几种属性,前缀是cpuinfo的属性文件表示硬件支持的几种属性。cpuinfo是scaling的子集,因为软件设置范围在硬件支持范围内。 scaling_governor 可以手动修改设置:
echo ondemand > /sys/devices/system/cpu/cpu0/scaling_governor
一般系统启动默认为performance,支持5种模式,可以通过make menuconfig配置。
目前DVFS支持调频调压策略主要就是上面支持的5种:
-
userspace(用户定义的) 使用用户在/sys 节点scaling_setspeed设置的频率运行。 最早的 cpufreq 子系统通过 userspace governor 为用户提供了这种灵活性。系统将变频策略的决策权交给了用户态应用程序,并提供了相应的接口供用户态应用程序调节 CPU 运行频率使用。 (可以使用Dominik 等人开发了cpufrequtils 工具包 )
-
performance cpu(突出性能) 按照支持的最高频率运行
-
ondemand(按需的) 系统负载小时以低频率运行,系统负载提高时按需提高频率 userspace是内核态的检测,效率低。而ondemand正是人们长期以来希望看到的一个完全在内核态下工作并且能够以更加细粒度的时间间隔对系统负载情况进行采样分析的governor。
-
conservative (保守的) 跟ondemand方式类似, 不同之处在于提高频率时渐进提高,而ondemand是跳变提高,ondemand比conservative先进,是conservative的改良版本。 ondemand governor 的最初实现是在可选的频率范围内调低至下一个可用频率。这种降频策略的主导思想是尽量减小对系统性能的负面影响,从而不会使得系统性能在短时间内迅速降低以影响用户体验。但是在 ondemand governor 的这种最初实现版本在社区发布后,大量用户的使用结果表明这种担心实际上是多余的, ondemand governor在降频时对于目标频率的选择完全可以更加激进。因此最新的 ondemand governor 在降频时会在所有可选频率中一次性选择出可以保证 CPU 工作在 80% 以上负荷的频率,当然如果没有任何一个可选频率满足要求的话则会选择 CPU 支持的最低运行频率。大量用户的测试结果表明这种新的算法可以在不影响系统性能的前提下做到更高效的节能。在算法改进后, ondemand governor 的名字并没有改变,而 ondemand governor 最初的实现也保存了下来,并且由于其算法的保守性而得名conservative 。 Ondemand降频更加激进,conservative降频比较缓慢保守,事实使用ondemand的效果也是比较好的。
-
powersave cpu(省电的) 以支持的最低频率运行 CPU会固定工作在其支持的最低运行频率上。因此其和performance这两种 governors 都属于静态 governor ,即在使用它们时 CPU 的运行频率不会根据系统运行时负载的变化动态作出调整。这两种 governors 对应的是两种极端的应用场景,使用 performance governor 体现的是对系统高性能的最大追求,而使用 powersave governor 则是对系统低功耗的最大追求。
-
schedutil:通过将自己的调频策略注册到hook,在负载发生变化的时候,会调用该hook,此时就可以进行调频决策或执行调频动作。前面的调频策略都是周期采样计算cpu负载有滞后性,精度也有限,而schedutil可以使用PELT(per entity load tracking)或者WALT(window assist load tracking)准确的计算task的负载。如果支持fast_switch的功能,可以在中断上下文直接进行调频。
功耗:performance > ondemand > conservative >powersave
2.3 主要数据结构
image.png
2.3.1 驱动相关cpufreq_driver
在include/linux/cpufreq.h中,用于描述cpufreq的驱动,是驱动工程师最关注的结构。如下默认值:
static struct cpufreq_driver d