Skip to content

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",也可能是无效并发(过度加锁、忙等、冗余任务);需要结合业务预期判断。

iOS CPU 与调度的 Process / Scheduler / Device 三层关系

图: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 核性能相同。

APP CPU 的 process scope 与 Total CPU Per-Core Usage 的 device scope 对比
APP CPU 描述目标进程 CPU 使用率;TotalCPU 与 Per-Core Usage 描述整机。看到某个 Per-CPU 条目接近 100% 既不能确认其 P/E 核身份,也不能单独证明负载来自目标 App 的某个线程。

3. 本领域指标地图 ​

观测视角metricID作用域 (Scope )回答什么问题
APP CPUios.process.cpu.non_normalizedprocess目标进程 CPU 使用率多高
TotalCPUios.device.cpu.total / .normalizeddevice设备整机(含系统与其他进程)负载多高
CPU Core Usageios.device.cpu.core_usagedevice分核 / Per-CPU 条目使用率如何分布
Thread Countios.process.thread.countprocess目标进程当前线程数快照
Context Switch Rateios.process.scheduler.context_switch_rateprocess目标进程线程切换频率
Interrupt Wakeup Rateios.process.scheduler.interrupt_wakeup_rateprocessintWakeups 累计字段的差分速率

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 与技术演进入口 ​

12. 继续阅读 ​

Perfowl · Performance Observer