本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
目前手机的 CPU 都是多核的,比如骁龙 8gen3 这款 CPU 就有 8 个核心,其中大核 Cortex-X4 的性能最好,时钟周期频率为 3.3GHZ,其他核心的性能就要差很多,其中两颗小核 Cortex-A520 的时钟频率只有 2.27GHZ。如果用大核来执行主线程,自然会让主线程在执行 UI 渲染等逻辑时拥有更快的速度。
一 线程绑核函数
Linux 系统提供了 pthread_setaffinity_np 和 sched_setaffinity 这两个函数用于将指定的线程绑定到指定的核心上,但是在 Android 系统中,屏蔽了 pthread_setaffinity_np 函数的使用,所以我们只能通过 sched_setaffinity 函数来进行绑核操作,函数如下:
#include <sched.h>
int sched_setaffinity(pid_t pid, size_t cpusetsize,cpu_set_t *mask);
- 第一个入参 pid 指的是线程的 id,如果 pid 的值为 0,则表示是主线程
- 第二个入参 cpusetsize 是第三个入参 mask 的长度
- 第三个入参 mask 是需要绑定的 CPU 序列的掩码
通过这个函数实现线程绑核的代码如下:
void bindCore(int coreNum){
cpu_set_t mask; //CPU核的集合
CPU_ZERO(&mask); //将mask置空
CPU_SET(coreNum,&mask); //将需要绑定的CPU核序列设置给mask,核为序列0,1,2,3……
if (sched_setaffinity(0, sizeof(mask), &mask) == -1){ //将主线程绑核
printf("bind core fail");
}
}
通过上面的代码中,便实现了将主线程绑定到了核心序列为 coreNum 的 CPU 核上。我们接着还需要进一步确定哪一个 CPU 核心为大核心。
二 获取大核序列
通过 /sys/devices/system/cpu/ 目录下的文件,可以查看当前设备有几个 CPU 核心 。笔者用来测试的是一台 Pixel3,可以看到有 cpu0 到 cpu7 共 8 个 CPU 核心。
/sys/devices/system/cpu $ ls
core_ctl_isolated cpu1 cpu3 cpu5 cpu7 cpuidle hang_detect_gold hotplug kernel_max offline possible present
cpu0 cpu2 cpu4 cpu6 cpufreq gladiator_hang_detect hang_detect_silver isolated modalias online power uevent
接着进入到某个 CPU 核心对应的 cpufreq 文件,即可查看具体某个 CPU 核心的详细参数,下面是序列为 0 的 CPU 核心的详细数据:
/sys/devices/system/cpu/cpu0/cpufreq $ ls
affected_cpus cpuinfo_max_freq cpuinfo_transition_latency scaling_available_frequencies scaling_boost_frequencies scaling_driver scaling_max_freq scaling_setspeed stats
cpuinfo_cur_freq cpuinfo_min_freq related_cpus scaling_available_governors scaling_cur_freq scaling_governor scaling_min_freq schedutil
该文件下的 cpuinfo_max_freq 就是当前 CPU 核心的时钟周期频率。下面是笔者的测试机 Piexl3 骁龙 845 芯片的每个核的时钟周期频率。可以看到 4、5、6、7 序列都是大核,时钟频率为 2.8GHZ,而其他的小核只有 1.7GHZ。
/sys/devices/system/cpu $ cat cpu0/cpufreq/cpuinfo_max_freq
1766400
/sys/devices/system/cpu $ cat cpu1/cpufreq/cpuinfo_max_freq
1766400
/sys/devices/system/cpu $ cat cpu2/cpufreq/cpuinfo_max_freq
1766400
/sys/devices/system/cpu $ cat cpu3/cpufreq/cpuinfo_max_freq
1766400
/sys/devices/system/cpu $ cat cpu4/cpufreq/cpuinfo_max_freq
2803200
/sys/devices/system/cpu $ cat cpu5/cpufreq/cpuinfo_max_freq
2803200
/sys/devices/system/cpu $ cat cpu6/cpufreq/cpuinfo_max_freq
2803200
/sys/devices/system/cpu $ cat cpu7/cpufreq/cpuinfo_max_freq
2803200
所以在代码实现中,只需要遍历 /sys/devices/system/cpu/ 目录下的 CPU 节点,然后读取 cpuinfo_max_freq 文件的值就能找到大核了,下面是详细的代码实现:
- 统计该设备 CPU 有多少个核。
int getNumberOfCPUCores() {
int cores = 0;
DIR *dir;
struct dirent *ent;
if ((dir = opendir("/sys/devices/system/cpu/")) != NULL) {
while ((ent = readdir(dir)) != NULL) {
std::string path = ent->d_name;
if (path.find("cpu") == 0) {
bool isCore = true;
for (int i = 3; i < path.length(); i++) {
if (path[i] < '0' || path[i] > '9') {
isCore = false;
break;
}
}
if (isCore) {
cores++;
}
}
}
closedir(dir);
}
return cores;
}
- 读行遍历每个核,找出时钟频率最高的那个核。
int getMaxFreqCPU() {
int maxFreq = -1;
for (int i = 0; i < getNumberOfCPUCores(); i++) {
std::string filename = "/sys/devices/system/cpu/cpu" +
std::to_string(i) + "/cpufreq/cpuinfo_max_freq";
std::ifstream cpuInfoMaxFreqFile(filename);
if (cpuInfoMaxFreqFile.is_open()) {
std::string line;
if (std::getline(cpuInfoMaxFreqFile, line)) {
try {
int freqBound = std::stoi(line);
if (freqBound > maxFreq) maxFreq = freqBound;
} catch (const std::invalid_argument& e) {
}
}
cpuInfoMaxFreqFile.close();
}
}
return maxFreq;
}
至此,便找出了大核的序列,然后调用 sched_setaffinity 进行绑核即可。除了主线程,也可以根据业务需要,将其他核心线程,比如渲染线程等线程绑定大核。当我们通过上面的逻辑将主线程绑定大核后,可以通过抓取 Trace 到 Pefetto 来验证目标线程运行在哪个核上,以此来确认是否绑定成功。