深入浅出CPUFreq

现在我们在购买一款手机的时候,大家都会去看一下这款手机所采用的芯片型号,有几个CPU核心(是8核处理器还是4核处理器),CPU的主频最高是多少。这些都是一些关系到性能体验的初步的硬件基础参数。

深入浅出CPUFreq

在整个手机处理器的发展历程过程中,经历了从最初的单核心处理,到多核心处理以及现在大小核架构的历程。CPU的主频也从最初的几十MHz,到几百MHz,直到现在的几个GH

深入浅出CPUFreq

图一:手机发展史也是一部处理器发展史

今天就给大家介绍一下CPU处理器的频率。首先介绍一下整个CPU系统的拓扑结构。处理器发展到今天,在手机这种对于性能与功耗要求极高,同时电池容量又受到限制的产品类型上,一般都是采用了B-L(Big-Little)架构,即大小核架构。小核也被称为功耗核心(Power Core),其性能稍弱,但是耗电比较低,通常被用来处理诸如电子书、浏览器此类对性能要求较低的轻负载场景;大核也被称为性能核心(Performance Core),其性能较强,但是耗电也高,被用来处理MOBA(Multiplayer Online Battle Arena,多人在线战术竞技游戏)游戏、相机等对于性能要求较高的高负载场景。

深入浅出CPUFreq

图二:CPU拓扑结构示例

以上图的这款8核心(4个功耗核心+4个性能核心)的CPU处理器为例,8个CPU核心被分为了2个簇(cluster),其中4个功耗核心在同一个簇上,另外4个性能核心在同一个簇上。基于成本考虑,一般同一个簇上的CPU都由同一个时钟源(clock)提供时钟输入,并由同一路regulator电源进行供电。

深入浅出CPUFreq

图三:同一个簇由同一个时钟源输入,同一个电源供电

那么为什么CPU需要调频?

摘抄一段关于DCVS的描述“dynamic clock and voltage scaling (DCVS) is a technique used to adjust the frequency and voltage of the power equation to deliver the needed performance at the ideal power level.“, 很显然,调频是为了平衡性能与功耗。如果CPU始终运行在高频率上,那么就造成功耗的浪费,特别是对于手持类终端设备,其电池容量本身受到了限制。

上面讲到了一些CPU相关的硬件基础知识。硬件是软件实现的基础,软件为硬件服务。下面我们回到软件层面。

深入浅出CPUFreq

图四:CPUFREQ软件框架图

CPUfreq框架大体可以分为下面几个模块:

1) 驱动层:提供不同类型CPU的调节频率的驱动能力

2) Governor:提供动态调频的算法

3) 核心层:计算机领域的任何问题都可以通过增加一个间接的中间层来解决,CPUfreq核心层其实就是一个中间层,向下提供不同类型CPU驱动的框架接口(注册cpufreq_driver驱动),向上提供不同调频governor的算法接口(注册cpufreq_governor)。同时统一提供对外的API跟sysfs调试接口。我们在进行一些性能功耗的基础调试的时候,会用到了一些sysfs debug的的接口,比如

深入浅出CPUFreq

通过主动提升频率来初步的进行问题的界定与排查。

CPUFreq 框架层

从图四的CPUFREQ软件框架图可以看出,框架层是一个虚拟中间层(cpufreq.c cpufreq_stats.c cpufreq_times.c freq_table.c提供基本函数,统计cpufreq相关信息),提供了承上启下的能力。另外提供了对外的API及能力。

在/sys/devices/system/cpu目录下可以看到框架层所提供的CPU频率的相关信息。

CPUFREQ软件框架

CPUFREQ软件框架

上面是关于CPUfreq框架层对外暴露的一个接口及参数信息。内核中采用struct cpufreq_policy来表示某一种CPU的频率策略。由于存在多个CPU由同一个时钟源提供输入的情况。一般struct cpufreq_policy被用来代表一个同一个cluster的时钟策略。struct cpufreq_policy中各个字段变量的描述如下

CPUFREQ软件框架

我们从一个简单的命令开始,来整体看一下CPUFREQ框架。

CPUFREQ软件框架

通过代码我们可以看到最终调用了cpufreq_set_policy函数进行参数的调节,最新的kernel 5.4的代码改成了cpufreq QoS,具体可以看本公众号之前的文章《Linux PM QOS介绍》,在此不再赘述。

CPUFREQ软件框架

除此之外框架层还提供了一些对于cpu时间及频率的相关统计,我们可以看到CPU在某一段时间内在每一个频率点上的驻留时间,用于具体问题的分析。

CPUFREQ软件框架

CPUFreq 驱动层

核心提供API cpufreq_register_driver来用于注册具体的cpufreq的驱动。以高通平台为例,其在qcom-cpufreq-hw.c中的probe函数进行了驱动的注册。

CPUFreq 驱动层

我们回过头来看一下驱动程序到底提供了哪些能力与信息。

CPUFreq 驱动层

CPUFreq 驱动层

其他的在struct cpufreq_driver中不常用的一些接口函数就不详细介绍了。

驱动层除了提供这些能力之外,也提供了一些CPU相关的基础信息。比如芯片所支持的最高频率,最低频率及所有支持的频率点,这些信息来源于驱动层提供。

CPUFreq 驱动层

CPU调频器

讲完框架层(提供框架支持以及调频策略)及驱动层(提供驱动能力支持),终于来到了本讲内容的重点:CPU调频器。Governor其实我们平时所说的DCVS,当然DVFS包含了硬件的调频能力与软件的算法。本讲集中在软件算法部分。核心层提供了API cpufreq_register_governor来进行注册。通过查看scaling_available_governor可以看到所有已经注册的调频器,通过设置scaling_governor可以进行各种governor之间的切换。

CPU调频器

图五:DCVS与governor

框架层通过cpufreq_policy设置了上层约束,governor在这个约束的范围内通过采集CPU的使用情况来决定是否需要调频,调到多少的频率合适。

CPU调频器

图六:governor的输入与输出

CPU调频器

图七:CPU使用率

下面介绍几种常用的governor:

CPU调频器

interactive

基于linux模块化的设计,早期的governor基本都是对CPU的使用率进行定时采样检查,然后根据使用率的情况调整CPU频率。Interactive governor作为早期android的调频器,也属于此类。它除了周期性采样CPU的使用率之外,还加入了一些优化项,来满足交互式终端的性能体验需求。

interactive

图八:interactive governor工作原理(tips:不要把任务都放在同一个进程里面,尽量分散到多个线程上)

Interactive governor针对android这类交互终端做了一些改进如下:

1) 引入hi_speed的概念,当CPU使用率超过一定阈值(go_hispeed_load)的时候,会直接跳到预先设定的hi_speed的CPU频率,来满足交互的性能。避免governor周期采样调频的时间延迟。

2) 引入target_loads的概念,一般governor都默认采用80%作为调频的阈值,即CPU使用率超过80%,向上提高频率;低于80%,向下降低频率。但是具体的CPU的个体上存在差异,比如高频率部分的能效比较差,低频段的能效比较好。可以通过target_loads来设定不同频率区间的调频阈值。

3) 引入above_hispeed_delay,在hispeed freq以上的部分,CPU频率需要在当前频率上驻留一定的时间才可以继续向上调频,用于功耗优化。

4) boost/boostpulse_duration: boost模式,上层应用程序按需触发boost模式。

5) 关于输入参数cpu 使用率,最初的interactive governor参数为CPU运行时间跟处于IDLE的时间。也就是说usage使用率处于0%~100%之间。设想一个大任务或类while(1)循环的场景,其负载始终处于100%。以target load处于80%为例,每次只能向上增加25%的频率。以这样的速度调频的话,什么时候才能调到最高频率呢?所以后来将interactive的CPU使用率参数从cputime切换成scheduler调度器统计的CPU使用负载(高通在walt的时候引入)。

schedutil

之前我们介绍过:CPU调速器schedutil原理分析

上面讲interactive的部分分析了其优缺点,虽然可以采用scheduler统计的负载来代替cputime的使用率,但是interactive仍然存在一个问题,即采样周期。由于interactive是周期性采样调频的,其结果必然受到了采样周期的影响。采样周期短的话,其响应更加及时,但是系统开销大;采样周期长的话,响应比较缓慢,但是系统开销比较小。随着屏幕的刷新率从60HZ提到了90HZ, 120HZ。每一帧绘制的时间越来越短,对于CPU频率的响应延迟要求越来越高。

基于上述的一些原因,引入了schedutil调频器。虽然其名字叫schedutil,即来自于scheduler的utilization,但是这一点在schedutil出现之前高通已经在interactive上引入了。个人认为schedutil最大的改动在于将周期性的采样触发改成了基于事件驱动触发(反模块化)。这些事件来自于scheduler。其工作原理大体如下:

1) 当一个任务被唤醒时,任务被放入到了某个CPU的队列中。此时意味着当前CPU的任务负载变重,需要及时的提高频率来保证性能。于是主动触发了调频。

2) 当一个任务完成工作,进入休眠状态,从任务队列中被移除。此时意味着当前CPU的任务负载变轻,需要及时的降低频率来降低功耗。于是主动触发了调频。

上面是2个典型的场景,诸如此类。当CPU上任务负载发生变化的时候,即由scheduler触发频率的调整。

cpufreq_schedutil.c中注册了hook函数

schedutil

scheduler在某些事件发生的时候主动调用cpufreq_update_util来进行频率的调整。其实已经在调度器跟调频器之间搭了座桥梁。

总结

本文主要介绍了CPU频率相关的硬件基础、拓扑结构,介绍了CPUFRE的整体软件框架,调频器的工作原理,android上的调频器的发展历史。

本文是基于OPPO的开源的kernel-4.19的代码进行分析的。https://github.com/oppo-source/kernel_msm-4.19。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程