性能优化02.4:Time Profiler

张建 lol

Time Profiler简介

Time Profiler 见名知意:CPU分析工具-时间分析工具,它会按照设定的时间间隔(默认1毫秒)来跟踪每一线程的堆栈信息(stacktrace),并通过比较时间间隔之间的堆栈状态,来推算出某个方法执行了多久,给出一个近似值。

Time Profiler使用须知

当点击 Time Profiler 应用程序开始运行后,就能获取到整个应用程序运行消耗时间分布和百分比.为了保证数据分析在统一使用场景真实行有如下点需要注意:

  • 在开始进行应用程序性能分析的时候,一定要使用 真机,模拟器运行在Mac上,然而Mac上的CPU往往比iOS设备要快。相反,Mac上的GPU和iOS设备的完全不一样,模拟器不得已要在软件层面(CPU)模拟设备的GPU,这意味着GPU相关的操作在模拟器上运行的更慢,尤其是使用CAEAGLLayer来写一些OpenGL的代码时候. 这就导致模拟器性能数据和用户真机使用性能数据相去甚运.

  • 另外在开始性能分析前另外一件重要的事情是,应用程序运行一定要 Release版本 而不是 Debug版本

  • 在发布环境打包的时候,编译器会引入一系列提高性能的优化,例如去掉调试符号或者移除并重新组织代码,另iOS引入一种 Watch Dog [看门狗]机制。不同的场景下,看门狗 会监测应用的性能。如果超出了该场景所规定的运行时间,看门狗 就会强制终结这个应用的进程,开发者可以 crashlog 看到对应的日志,但Xcode在调试配置下会禁用 Watch Dog

Time Profiler的使用

  1. 首先用Xcode打开你的项目。比如我的项目 FJReading

  2. 修改 Edit SchemeRelease 版本如下:

因为Release环境下的才是正确的配置:

  1. 打开Instrument -> Time Profiler

  1. 界面的整体如下:

  • weight:整体运行时长和比例
  • self weight:自己代码的运行时长和比例
  • Symbol Name:被调用函数的符号信息,可以切换为实际调用的方法
  1. 选择真机和你要调试的App

  1. 点击Start按钮,Time Profiler就开始记录App的运行情况

  1. 可以看到在CPU使用过高的位置对应的具体调用栈

打开Time Profiler调试工具进入到调试界面,选择你的真机和项目,点击左上角红色原点(启动程序按钮)之后便可以看到如下图的时间消耗:

  1. CallTree选项介绍

但是我们发现这些信息只能显示到底层的线程Runloop耗时,并不能帮助我们定位到具体的代码中去,下面介绍CallTree选项的勾选操作以及含义。这些选项默认是不选的,但把它们勾选上可以帮你更快定位到关键的代码上,往往这也是问题的源头。

  • Separate by Thread(建议选择):按线程分开做分析,这样更容易揪出那些吃资源的问题线程。特别是对于主线程,它要处理和渲染所有的接口数据,一旦受到阻塞,程序必然卡顿或停止响应。

  • Invert Call Tree(建议选择):反向输出调用树。把调用层级最深的方法显示在最上面,更容易找到最耗时的操作。

  • Hide Missing Symbols(建议选择):隐藏缺失符号。如果dSYM文件或其他系统架构缺失,列表中会出现很多奇怪的十六进制的数值,用此选项把这些干扰元素屏蔽掉,让列表回归清爽。

  • Hide System Libraries(建议选择):隐藏系统库文件。过滤掉各种系统调用,只显示自己的代码调用。

  • Flattern Recursion(一般不选):拼合递归。将同一递归函数产生的多条堆栈(因为递归函数会调用自己)合并为一条。

  • Top Functions(可选):找到最耗时的函数或方法。

  1. 根据这些时间对应代码便可以做一些优化,减少重复的耗时逻辑优化CPU。我用上面的流程查看了我写的代码基本上是没有太耗时的操作,perfect。

写一个demo具体定位耗时

  1. 创建一个demo项目,写如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)viewDidLoad {
[super viewDidLoad];

//单元测试
NSDate * startDate = [NSDate date];
for (int i = 0; i < 100000; i++) {
NSString *string = @"123";
string = [string stringByAppendingString:@"abc"];
NSLog(@"%@", string);
}
NSDate *endDate = [NSDate date];
NSTimeInterval interval = [endDate timeIntervalSinceDate:startDate];
NSLog(@"time = %f",interval);
}
  1. 我们查看一下CPU耗时操作:

可以看到ViewController中占用大量的时间763ms,双击函数调用进入里面看看:

  1. 我们注释掉打印重新跑一遍代码看看效果
1
2
3
4
5
6
7
for (int i = 0; i < 100000; i++) {
@autoreleasepool {
NSString *string = @"123";
string = [string stringByAppendingString:@"abc"];
// NSLog(@"%@", string);
}
}

查看结果如下:

结论:由此可见,耗时操作明显下降了。

  • Post title:性能优化02.4:Time Profiler
  • Post author:张建
  • Create time:2020-08-14 12:12:30
  • Post link:https://redefine.ohevan.com/2020/08/14/OC性能优化/性能优化02.4:Time Profiler/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.