最近在做性能监控,思路大致是 hook 一些 cpu 耗时比较严重(layoutSubviews,cellForRow 等)的方法,获取方法调用的时间写入日志。后期对日志进行分析,得到优化的方向。
此文简单介绍一下整套系统的设计思路和简述实现细节。
设计原则
1.不能影响原有业务代码逻辑
我们只是一个观察者,不应该影响其他业务方正常的功能。
2.监控对性能影响尽可能小
显而易见,因为观察者效应,监控必然是会对原有性能有影响。这一点需要非常注意
3.最好不需要业务方书写代码
如果需要业务方自己书写代码,这一点非常麻烦。最好能做到无痕接入,尽可能通用、可配置。
*4.面对不同用户提供不同程度的能力
使用这套性能监控的人可能很多。
- 产品
- QA
- RD
为这三种人提供分层次的能力。
5.最好有日志图表化的能力
日志数据分析是很重要的
6.数据一定要准确
一定要拿出可信度很高的数据,推动项目优化
项目实现
第一期:从用户肉眼感知到卡顿改善为可以通过数据去分析
第二期:页面级别统计流畅度,反馈给相关负责人
第三期:添加运行时的调试工具,帮助调试(自定义 time profiler)
统计方法调用时长
运行时懒加载 swizzle 我们关心的方法: layoutSubviews,drawRect,cellForRow
懒加载保证了我们调用时机永远在调用栈最底下,这样我们在方法被调用之前记录一个时间(start time),原始实现调用之后记录一个时间(end time),得到一个方法调用时长(duration)。
卡顿可视化
掉帧是一个主观性很强的感觉。我们需要把掉帧这种行为用数据、颜色表示出来。
特别是当一个列表页大部分情况下很流畅,但是只在一瞬间掉一两帧,这种情况直接用 Instruments 很难分析出需要优化的地方。
所以设计了如下的方案,完美解决瞬间掉帧的事情。
添加 FPS lable: 提供两个CPU、GPU 两种帧率显示
GPU 帧率根据每次 displayLink 到来的时候通过私有 API 获取的真实的 CAFrameID 计算得出
每个 Cell 添加耗时 Label
动态给每个 Cell 添加几个 Layer,放在左上角。显示 cellForRow 调用时长、所有子 View layoutSubviews 等方法调用的时长
UIView 染色
View 根据 layoutSubiew 和 drawRect 方法调用的时长进行染色(非常耗时的涂红,不太耗时涂白)
/// 0 <= costLevel <= 1
layer.backgroundColor = [r: 1 g:1-costLevel b:1-costLevel]
布局、绘制行为记录
记录运行时所有的行为,方便分析
抓取业务数据
卡顿的瞬间记录 Cell 层次结构
[view recursiveDescription]
label text 等信息
日志
记录规则
日志按 displayLink 一次为一个单位。记录了每个 View 的布局、绘制,cellForRow 等行为
持久化
持久化在本地。提供直接在 app 内部查看 log 的能力
日志传输 在 pc 搭建了一个服务器,监控系统内部提供连接该服务器传输文件、流的能力。
因为日志量太大,没办法直接走后端接口上传,转而用自己 pc 搭建服务器的形式。
统计上报
采集标志性的数据上报服务器,后端分析通知相关负责人页面流畅度。
网友评论