在 Android 应用中配置 CPU 调度策略,可通过以下方法优化线程执行优先级和资源分配:
一、配置线程优先级(用户态)
1. 通过 android.os.Process 设置线程优先级
// 设置当前线程优先级(范围:-20 ~ 19,值越小优先级越高)
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
// 设置指定线程优先级(需在线程启动后调用)
android.os.Process.setThreadPriority(int tid, int priority);
预定义优先级常量:
- THREAD_PRIORITY_DEFAULT (0)
- THREAD_PRIORITY_LOWEST (19)
- THREAD_PRIORITY_BACKGROUND (10)
- THREAD_PRIORITY_URGENT_AUDIO (-19)
2. 使用 Linux nice 值
// Native 代码(需 NDK)
#include <sys/resource.h>
setpriority(PRIO_PROCESS, tid, -10); // 设置优先级(需 root 权限)
注意: Android 系统一直在做进程优先级调度的相关改进工作,特别是Android 10 之后的版本。线程优先级设置后不一定会起作用,因为Android 系统会根据当前运行的应用状态,动态设置各个应用的线程优先级。
二、配置实时调度策略(需系统权限)
1. 设置 SCHED_FIFO/SCHED_RR 策略
#include <pthread.h>
struct sched_param param;
param.sched_priority = 50; // 优先级范围:1~99(值越大优先级越高)
pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m);
限制:
- 需要 CAP_SYS_NICE 能力(系统签名应用或 root 权限)
- Android 8.0+ 普通应用调用会失败(EPERM 错误)
2. 绕过限制的临时方案
# 通过 ADB 临时提升进程优先级(需 root)
adb shell "echo -n <pid> > /dev/cpuctl/foreground/tasks"
三、利用 Android 平台特性
1. 前台服务优先级
从 Android 10(API 级别 29)开始,Google 引入了 foregroundServiceType 属性,用于声明前台服务的用途。
<!-- AndroidManifest.xml 声明前台服务类型 -->
<service
android:name=".MyForegroundService"
android:foregroundServiceType="mediaPlayback" />
前台服务默认分配至 top-app cgroup,CPU 资源优先级高于后台进程。
2. 绑定到高性能核心
现代CPU都有多个核心,而且有大核与小核的区别,所以我们可以通过设置不同线程的cpu亲和性来确保线程调度在固定的核上。 优化思路:
- IO密集型的线程可以绑定在小核上,让出大核。
- 短时计算形任务可以绑定在小核上。
- 重要的计算线程可以绑定在大核上。
- 内存占用量特别大的重要线程,可以绑定在一个大核上,确保命中L1缓存和运行速度。
// 读取CPU 核心编号和频率等信息,可以用来判断大小核心。
// 详细代码可以参考:https://github.com/fcarucci-zz/peuck/blob/master/src/Peuck.cpp
void ReadCpuInfo() {
char buf[10240];
FILE *fp = fopen("/proc/cpuinfo", "r");
if (fp) {
std::stringstream ss;
while (fgets(buf, 10240, fp) != NULL) {
buf[strlen(buf) - 1] = '\0'; // eat the newline fgets() stores
std::string line = buf;
if (startsWith(line, "processor")) {
ss.str("");
ss << "/sys/devices/system/cpu/cpu" << cpu.cores.size() << "/cpufreq/cpuinfo_max_freq";
auto frequency = ReadFile(ss.str());
ss.str("");
ss << "/sys/devices/system/cpu/cpu" << cpu.cores.size() << "/topology/physical_package_id";
auto package_id = ReadFile(ss.str());
Cpu::Core core;
core.id = cpu.cores.size();
core.frequency = std::atoi(frequency.c_str());
core.package_id = std::atoi(package_id.c_str());
cpu.cores.push_back(core);
}
if (startsWith(line, "Hardware")) {
cpu.hardware = split(line, ':')[1];
}
// /sys/devices/system/cpu/cpu4/cpufreq/cpuinfo_max_freq
// ALOGE("[%d]: %s", cpu.cores, line.c_str());
}
fclose(fp);
ss.str("");
for (const auto& core : cpu.cores) {
ss << core.id << " " << core.frequency << " " << core.package_id << std::endl;
}
cpu.topology = ss.str();
}
ALOGE("CPU cores = %d", (int) cpu.cores.size());
ALOGE("CPU hardware = %s", cpu.hardware.c_str());
}
// 设置线程 CPU 亲和性
#include <sched.h>
#include <sys/types.h>
#include <unistd.h>
pid_t my_pid; // PID of the process containing your thread.
// Assumes that cpu0, cpu1, cpu2, and cpu3 are the "slow CPUs".
cpu_set_t my_cpu_set;
CPU_ZERO(&my_cpu_set);
CPU_SET(0, &my_cpu_set);
CPU_SET(1, &my_cpu_set);
CPU_SET(2, &my_cpu_set);
CPU_SET(3, &my_cpu_set);
sched_setaffinity(my_pid, sizeof(cpu_set_t), &my_cpu_set);
四、通过 cpuset 控制 CPU 资源分配
1. 查看当前 cgroup
adb shell "cat /proc/pid/cpuset" # 输出如:/top-app
2. 修改进程 cgroup
#将进程移至 foreground 组(需 root)
adb shell "echo pid > /dev/cpuctl/foreground/tasks"
常用 cgroup 路径:
- /dev/cpuctl/foreground:前台进程
- /dev/cpuctl/background:后台进程
- /dev/cpuctl/top-app:最高优先级进程
注意:Android 系统会根据当前应用的实际运行情况,来动态设置cgroup,开发者一般不要设置。
五、最佳实践与注意事项
- 避免滥用高优先级设置,有可能会影响系统的总体任务调度,反而降低性能。
- 可以根据线程的工作内容,仔细调整绑定cpu核心的策略,来优化APP的运行效率。
调试工具
- 查看线程调度策略:
adb shell "ps -T -p <pid>" # 显示线程优先级(PRI列)
- 监控 CPU 频率:
adb shell "cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq"