外观
iOS CPU 与调度总览:CPU 使用率、QoS 与分核观测边界
先看结论
- APP CPU 描述目标进程的 CPU 使用率,不能单凭它证明是哪个线程、哪段代码。 结合调用栈、Instruments Time Profiler 才能定位到函数级根因。
- QoS 不是"把某类任务固定分配到 P-Core 或 E-Core"的静态规则。 它是提交给系统调度器的优先级/能效提示,具体运行在哪个核心由内核结合系统负载、热状态等因素动态决定,且会随系统条件变化。
- Per-Core Usage 是 device scope,不是 process scope。 它能告诉你某个
PerCPUUsage条目使用率很高,但不能单独证明该条目对应哪个 P/E 核,也不能证明负载来自目标 App 的某个线程。 - 多核并发 CPU 超过 100% 不代表"良好利用了 GCD",也可能是无效并发(过度加锁、忙等、冗余任务);需要结合业务预期判断。
图:APP CPU 属于 process scope;TotalCPU / Per-Core 属于 device scope;QoS 是调度提示而不是核心绑定。
0. 当前实现摘要
Perfowl 的 CPU 观测体系全部基于 pymobiledevice3 原生 Sysmontap.create(provider, interval=...) 订阅 com.apple.instruments.server.services.sysmontap,按目标 PID 过滤得到进程级快照,同时该通道也上报设备级整机快照。领域内 4 个指标——APP CPU、TotalCPU、CPU Core Usage、以及从 system-condition-metrics.md 拆分出的 Thread Count / Context Switch Rate / Interrupt Wakeup Rate——共享同一采集窗口但回答不同问题。
1. 这个领域到底观察什么
CPU 领域回答“目标 App 的 CPU 使用率多高、系统整体 CPU 负载如何、分核使用率如何分布”,以及“线程数量、上下文切换和唤醒速率是否异常”。它不直接回答“是哪一行代码”——那需要 Instruments Time Profiler 或代码级证据。
2. iOS 系统原理:异构多核与 QoS 调度
现代 iPhone 采用 性能核(P-Core) 与 能效核(E-Core) 组成的异构多核架构。iOS 通过 QoS(Quality of Service) 向调度器表达任务的优先级与能效倾向(如 UserInteractive、UserInitiated、Utility、Background),但这是一套提示与优先级机制,不是把某个 QoS 等级硬编码绑定到某类核心的静态映射:
- 内核调度器会结合系统整体负载、热状态、电量模式等条件动态决定线程实际运行在哪个核心;
- 高 QoS 任务通常更容易被调度到高性能核心,但这是倾向性结果,不是每次都发生的确定性保证;
- 系统可能因为热节流、后台压力或电量模式改变实际调度结果。
参考:DispatchQoS。
在当前多核未归一化统计口径中,一个 CPU 的持续满载份额约记为 100%;如果设备有 6 个有效 CPU,聚合使用率在极端情况下可以接近 600%。因此 APP CPU 观测到 280%~300% 可以是正常的多核并发结果;这个百分比是CPU 使用率统计口径,不是芯片“算力”的物理单位,也不表示不同 P/E 核性能相同。
3. 本领域指标地图
| 观测视角 | metricID | 作用域 (Scope ) | 回答什么问题 |
|---|---|---|---|
| APP CPU | ios.process.cpu.non_normalized | process | 目标进程 CPU 使用率多高 |
| TotalCPU | ios.device.cpu.total / .normalized | device | 设备整机(含系统与其他进程)负载多高 |
| CPU Core Usage | ios.device.cpu.core_usage | device | 分核 / Per-CPU 条目使用率如何分布 |
| Thread Count | ios.process.thread.count | process | 目标进程当前线程数快照 |
| Context Switch Rate | ios.process.scheduler.context_switch_rate | process | 目标进程线程切换频率 |
| Interrupt Wakeup Rate | ios.process.scheduler.interrupt_wakeup_rate | process | intWakeups 累计字段的差分速率 |
4. Perfowl 当前怎样取得这些事实
全部 6 个指标共享 com.apple.instruments.server.services.sysmontap 通道,由 pymobiledevice3 原生 Sysmontap.create() 订阅,按 500ms 级窗口下发进程与设备双重快照。Thread Count 是瞬时 gauge 直读;CSwitch/Wakeup 是累加计数器,经 (current - previous) / elapsed 差分得到速率(详见 CPU / Sysmontap 技术演进与验证)。
5. Scope / Identity / Runtime Condition
- APP CPU、Thread Count、CSwitch、Wakeup 都是 process scope,经目标 PID 过滤;
- TotalCPU、Per-Core Usage 都是 device scope,反映整机(含系统进程、其他 App)负载,不能单独归因到目标 App 的具体线程;
- Per-Core 数据无法证明"某个核心上运行的就是目标 App 的某个线程"——这是本领域最容易被误用的一点,详见第 7 节。
6. 指标应该怎样组合阅读
推荐顺序:先看 APP CPU(进程 CPU 使用率)→ 对照 TotalCPU(系统整体是否也高)→ 需要观察分核形态时再看 Per-Core → 需要判断并发是否合理时结合 Thread Count / CSwitch / Wakeup。单独看任何一个指标都容易得出片面结论。
7. 常见异常形态:观察 → 候选原因 → 交叉验证 → 不能确认
| 观察到的波形 | 候选原因(需交叉验证) | 下一步验证 | 仅凭本指标不能确认的结论 |
|---|---|---|---|
| APP CPU 稳定在 95%~105%,某单核持续 100% | 主线程死循环、正则灾难性回溯、递归无退出条件等候选方向 | 结合调用栈 / Time Profiler 定位具体函数 | 不能仅凭波形断定是哪一行代码或哪个线程 |
| APP CPU 瞬间飙升至 300%~500% | 批量解码、无限制并发队列等候选方向 | 结合线程数、任务提交日志确认并发结构 | 不能仅凭峰值判断"并发设计不合理",需结合业务预期 |
| 页面静置但 APP CPU 持续 8%~25% | 未销毁的定时器、未停止的动效重绘、高频轮询等候选方向 | 结合 Wakeups、代码审查确认具体来源 | 不能仅凭 CPU 数值本身定位到具体组件 |
| Device CPU 高但 APP CPU 低 | 系统或其他进程负载高(如 iCloud 同步、OTA 下载)候选方向 | 清理后台环境复测、对比多轮分布 | 不能直接断言"与被测 App 完全无关"——同窗内其他因素也可能间接受影响 |
8. 连续性、质量、missing / 0 / warmup
- Thread Count 缺失时为
missing,不补 0; - CSwitch / Wakeup 首次观测为
warmup(无前值),计数器倒退(如应用重启)为counter-reset,均不产生负值或虚假尖峰; - 详见各指标 Canonical 页的"质量语义"章节。
9. 当前证据与平台覆盖
CPU 领域的 4 个已有指标(APP CPU/TotalCPU/CPU Core Usage)在多个 iOS 版本上有真机验证记录;Thread/CSwitch/Wakeup 拆分自 system-condition-metrics.md,证据现状见各自 Canonical 页,不做跨版本外推。
10. 常见误区
- 把 QoS 理解为"某类任务固定跑在某类核心"——见第 2 节;
- 把 Per-Core 某核 100% 直接当作目标 App 某线程的证据——它是 device scope;
- 把多核并发 CPU > 100% 当作"GCD 用得好"的证明——也可能是无效并发;
- 把 TotalCPU 高、APP CPU 低直接宣布"与被测 App 无关"——只能提示系统噪声,需要更多证据。
11. 指标 Canonical 与技术演进入口
- APP CPU、TotalCPU、CPU Core Usage
- Thread Count、Context Switch Rate、Interrupt Wakeup Rate
- CPU / Sysmontap 技术演进与验证
12. 继续阅读
- 跨指标诊断与证据升级:CPU 异常需要结合哪些其他领域证据。
- 技术阅读地图:如何按领域总览→单指标→技术演进→诊断的顺序继续阅读。