Time Profile
Time Profiler是Instruments工具包提供的,用来分析项目中代码的执行时间,能获取到整个应用程序运行中所消耗的时间分布和百分比, 我们可以找出导致程序运行慢的原因,即耗时的代码.
使用Time Profile前有两点需要注意的地方:
使用真机调试
因为使用模拟器是运行在Mac上的,Mac上的CPU往往比iOS设备要快。且Mac上的GPU和iOS设备的完全不一样,模拟器不得已要在软件层面(CPU)模拟设备的GPU,这意味着GPU相关的操作在模拟器上运行的更慢,尤其是使用CAEAGLLayer来写一些OpenGL的代码时候,这就导致模拟器性能数据和用户真机使用性能数据相去甚远
应用程序一定要使用发布配置
在发布环境打包的时候,编译器会引入一系列提高性能的优化,例如去掉调试符号或者移除并重新组织代码。另iOS引入一种"Watch Dog"[看门狗]机制,不同的场景下,“看门狗”会监测应用的性能,如果超出了该场景所规定的运行时间,“看门狗”就会强制终结这个应用的进程。开发者可以crashlog看到对应的日志,但Xcode在调试配置下会禁用"Watch Dog"
Time Profile原理
如果记录CPU使用情况,可以记录每个方法执行时间。然后根据调用堆栈把每个方法执行的时间累加起来,来反映CPU使用情况。Time Profile并没有事无巨细的记录每个方法的执行时间,而是使用了定时采样方式来反应CPU使用情况。
每隔1ms,Time Profile会记录一下当前执行堆栈,就像Xcode中断点调试那样的堆栈。如果1s内CPU都在忙碌,Time Profile会截取到1000个堆栈,这1s中的Call Tree就根据堆栈的调用结构组织起来的,所以Time Profile Call Tree和调用堆栈看起来很像,实际上Time Profile Call Tree等于每个时间点调用堆栈的叠加
如果把Time Line拉到足够大,还能看到样本标记,可以在Detail pane中选中sample来看采集到的样本
image.png在分析CPU图谱,有两个很重要的概念,weight 和 self
image.png
weight:指定时间,此方法在采样堆栈出现的次数。eg:weight 46ms,就是这段时间里,此方法被采样46次
self:指定时间,此方法处于堆栈最后面的次数。eg1:self 1ms,就是这段时间里,采样时,此方法在调用堆栈最后面1次;eg2:self weight 0ms,就是这段时间里,采样时此方法一次也没有在调用堆栈最后面,也就是这个方法并没有干实事,全是调用其他方法做事的
可以在底部对展示的内容进行筛选
image.png
- Separate by State : 程序的不同状态做区分(运行中,休眠,挂起)
- Separate by Thread:每个线程应该分开考虑,只有这样你才能揪出那些大量占用CPU的"重"线程
- Invert Call Tree:从上倒下跟踪堆栈,这意味着你看到的表中的方法,将已从第0帧开始取样,这通常你是想要的,只有这样你才能看到CPU中话费时间最深的方法,也就是说FuncA{FunB{FunC}} 勾选此项后堆栈以C->B-A 把调用层级最深的C显示在最外面
- Hide System Libraries:勾选此项你会显示你app的代码,这是非常有用的。因为通常你只关心cpu花在你自己写的代码上的时间 而不是花在系统代码上的时间
- Flatten Recursion:递归函数, 每个堆栈跟踪一个条目
- Top Functions:一个函数花费的时间直接在该函数中的总和,以及在函数调用该函数所花费的时间的总时间。因此,如果函数A调用B,那么A的时间报告在A花费的时间加上B花费的时间,这非常有用,因为它可以让你每次下到调用堆栈时挑最大的时间数字,归零在你最耗时的方法
实际使用
先使用instrument捕捉CPU使用情况,然后
在time line上选出想要分析的片段
选中想要分析的线程,分析卡顿就选中主线程
沿着占用CPU高的方法,一级一级展开call tree,分析调用
上图展示了CA::Layer::layout_if_needed(CA::Transaction*)
占据的时间,这是系统渲染的方法
继续往下找,能看到找到我们自己的方法了.
当用到系统控件渲染的时候
可以重写控件,给每个View起个标志性的名字,覆盖渲染方法,这样在Time Profile里就能区分各个控件的渲染耗时占比了