克魔助手
全部文章

没有 Mac,如何为 iPhone 做性能分析?

4 分钟阅读性能入门

我接触过的 iOS 团队中,有一半并不在 Mac 上开发。他们编写代码后提交给 CI,由 CI 在一台租用的 Mac 上打包。这套流程一直运转良好,直到有人问:「为什么在我的手机上这么卡?」而能回答这个问题的工具,只能在团队里没人拥有的机器上运行。

本文介绍在这种情况下有哪些可用的方法。

为什么最直接的方案行不通

Instruments 是官方的性能分析工具,功能完善,但有三个容易被忽略的前提:

  1. 只能在 macOS 上运行。
  2. 通常需要 Xcode 工程,至少需要一个可以附加的构建。
  3. 大多数常用的 instrument 要求使用开启调试的包。

第三点即使对有 Mac 的团队也是隐患。Debug 包的优化级别、日志,有时甚至代码路径都与正式包不同。分析它时,您测量的是一个没有用户在运行的程序。

设备本身已经在采集这些数据

讨论工具时常被忽略的一点是:iOS 本身就在测量这些指标。系统会跟踪每个进程的 CPU、内存占用、GPU 利用率、帧呈现、网络字节数和能耗归因。这是系统必须做的,因为调度、内存压力下的进程回收,以及「设置 → 电池」页面都依赖这些数据。

这些计数器通过设备自身的服务,向用户信任过的电脑开放。这与电脑为设备备份、安装 App 使用的是同一条通道。读取这些数据无需越狱,无需在设备上常驻程序,也无需在 App 中集成 SDK。

能获取哪些数据

连接设备并将范围设为某个 App 后,您可以实时读取:

  • 每个进程的 CPU(单核百分比)
  • 随时间变化的内存占用
  • GPU 利用率
  • 实际呈现的帧率
  • 网络收发字节数
  • 能耗影响

这并不能覆盖 Instruments 的全部功能:没有内存分配调用树,也没有精确到符号的耗时分析。但它足以回答大多数性能排查最初要回答的问题:瓶颈出现在哪种资源上,在什么时候?

这通常就是关键问题

一份卡顿反馈要回答的并不是调用树,而是:问题受限于 CPU、GPU,还是被其他因素阻塞。这三种情况的修复方向完全不同,而图表上的四条曲线足以区分:

卡顿时刻 受限于
CPU 升高、GPU 平稳 主线程上有繁重任务
GPU 升高、CPU 平稳 渲染开销:过度绘制、模糊、离屏渲染
内存骤升 一次大的内存分配,通常是图片解码
各项指标均无变化 阻塞在其他地方,或设备正在降频

确定属于哪一种情况,就知道该检查哪部分代码。之后往往不再需要调用树;即使需要,您也已经知道该把哪段交互交给有 Mac 的同事去分析。

一个容易被忽视的因素

在您手中的设备上做分析,价值不如在用户手中的设备上做分析。三年前的机型帧预算更紧张、更早降频、电池容量已经衰减,而桌上的旗舰机会掩盖这些问题。

因此,能在任意桌面系统上做性能分析,对没有 Mac 的团队而言不只是方便。这意味着您可以分析团队中的任何一台设备,例如测试同事的手机、售后备用机或闲置的旧机型,而不必局限于与某台 Mac 配对过的设备。

推荐的操作流程

  1. 拿到出现问题的那台设备,或您支持的最旧机型。
  2. 等设备冷却。发热的设备表现会明显不同。
  3. 将范围设为您的 App。
  4. 采集一段固定的交互,例如同一个列表、相同的滑动距离、持续 10 秒。
  5. 找到帧率下降的位置,查看该时刻的其他曲线。
  6. 只修改一处,然后重新采集同一段交互。
  7. 对比两次结果。

第 6 步最常被跳过。单次采集只是一个数字;改动前后的两次采集,才能得出结论。

它无法替代的部分

需要明确以下边界:

  • 内存分配分析。 无法从内存趋势中看出的泄漏,仍需借助堆分析工具。
  • 符号级 CPU 归因。 知道受限于 CPU,并不等于知道是哪个函数导致的。
  • 网络内容。 字节数不等于请求内容,后者需要抓包工具。

设备级计数器的作用,是把「感觉有点卡」转化为「在这款机型上,页面转场期间受限于 GPU」。性能排查的大部分价值就在这一步,而您在日常使用的电脑上即可完成。