1. Android CPU 使用率采集基础概念
在 Android 性能测试领域,CPU 使用率是最基础也是最重要的指标之一。但很多开发者仅仅满足于使用 top 命令看一眼表面数据,对背后的计算逻辑和数据来源并不了解。这种浅尝辄止的做法,往往会导致在性能分析和优化时出现误判。
1.1 CPU 使用率的本质
CPU 使用率本质上衡量的是"时间片的分配比例",而不是"算力的消耗比例"。Linux 内核使用称为 tick 的时间片来调度任务,每个 tick 的时长通常在 4-10 毫秒之间,具体取决于内核配置参数 CONFIG_HZ。
在每个 tick 结束时,内核会记录这个 tick 被分配到了哪个状态:
- 用户态(执行应用代码)
- 内核态(执行系统调用)
- 空闲状态(CPU 无事可做)
- 等待 I/O 状态(CPU 因等待 I/O 而空闲)
因此,CPU 使用率的精确定义应该是:
一段时间内,非空闲 tick 占总 tick 的比例
1.2 为什么需要理解底层原理
理解 CPU 使用率的计算原理对于性能测试至关重要,原因包括:
- 准确解读数据:知道数据如何产生,才能正确解读其含义
- 问题排查:当数据异常时,能够快速定位问题根源
- 工具选择:根据测试需求选择合适的采集工具和方法
- 性能优化:针对性地优化 CPU 使用率高的场景
2. CPU 使用率计算公式详解
2.1 数据采集的基本原理
Linux 内核提供的 CPU 统计数据是从系统启动开始的累积值。因此,要计算某段时间内的 CPU 使用率,必须进行两次采样,然后计算差值。
2.1.1 整机 CPU 使用率计算
计算步骤如下:
- 第一次采样:记录 total₁ 和 idle₁
- 等待 N 秒(通常 5-10 秒)
- 第二次采样:记录 total₂ 和 idle₂
- 计算差值:
- Δtotal = total₂ - total₁
- Δidle = idle₂ - idle₁
- 计算整机 CPU 使用率:
整机 CPU% = (Δtotal - Δidle) / Δtotal × 100
其中:
- total:所有状态 tick 的总和,包括 user、nice、system、idle、iowait、irq、softirq 和 steal
- idle:空闲 tick 数,直接取自
/proc/stat第一行的第 4 个字段
2.1.2 进程 CPU 使用率计算
计算步骤如下:
- 第一次采样:记录 process_time₁ 和 total₁
- 等待 N 秒
- 第二次采样:记录 process_time₂ 和 total₂
- 计算差值:
- Δprocess = process_time₂ - process_time₁
- Δtotal = total₂ - total₁
- 计算进程 CPU 使用率:
进程 CPU% = Δprocess / Δtotal × 100
其中:
- process_time:进程消耗的 CPU tick 总数,计算方式为 utime + stime
- Δtotal:与整机计算使用相同的分母
关键注意事项:进程 CPU% 的分母是整机的 Δtotal,而不是进程自己的 Δprocess。这意味着进程 CPU% 表示的是该进程在所有 CPU 资源中占用的比例。例如,在 4 核设备上,单线程进程的最高 CPU% 约为 25%,4 线程跑满时接近 100%。
2.2 公式背后的原理
这两个核心公式之所以能够准确反映 CPU 使用情况,是因为它们基于以下原理:
- 时间片统计:Linux 内核精确统计了每个 CPU 时间片的使用情况
- 累积计数:统计数据从系统启动开始累积,避免了计数器溢出的问题
- 差值计算:通过两次采样的差值,可以准确反映任意时间段内的 CPU 使用情况
值得注意的是,所有常见的 CPU 采集工具(如 top、dumpsys cpuinfo 和各种 APM SDK)底层都是基于这两个公式进行计算,只是封装程度不同而已。
3. 数据源解析与实操
3.1 整机数据源:/proc/stat
/proc/stat 是 Linux 内核提供的 CPU 统计接口,所有 Android 设备都具备。其第一行格式如下:
code复制cpu 10132153 290696 3084719 46828483 16683 0 25195 0 0 0
跳过开头的 cpu,后面 8 个数值分别代表:
| 位置 | 名称 | 含义 |
|---|---|---|
| 1 | user | 用户态时间(执行应用代码) |
| 2 | nice | 低优先级用户态时间 |
| 3 | system | 内核态时间(系统调用、驱动) |
| 4 | idle | 空闲时间(CPU 无事可做) |
| 5 | iowait | 等待 I/O 的空闲时间 |
| 6 | irq | 硬中断处理时间 |
| 7 |
