Android 应用的CPU调度策略优化

1,331 阅读4分钟

在 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, &param);

‌限制‌:

  • 需要 ‌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"