如何对 iOS 启动阶段耗时进行分析(点击这里查看原文)

 starming   2019-12-09 07:37   238 人阅读  0 条评论

前言

启动优化一役后,超预期将所负责的 App 双端启动的耗时都降低了65%以上,iOS 在iPhone7上速度达到了400毫秒以内。就像产品们用后说的,快到不习惯。由于 App 日活用户过亿,算一下每天为用户省下的时间,还是蛮有成就感的

启动阶段性能多维度分析

要优化,先要做到的是对启动阶段各个性能纬度做分析,包括主线程耗时、CPU、内存、I/O、网络。这样才能够更加全面的掌握启动阶段的开销,找出不合理的方法调用。启动越快,更多的方法调用就应该做成按需执行,将启动压力分摊,只留下那些启动后方法都会依赖的方法和库的初始化,比如网络库、Crash 库等。而剩下那些需要预加载的功能可以放到启动阶段后再执行。

启动有哪几种类型,启动有哪些阶段呢?

启动类型分为:

  • Cold:App 重启后启动,不在内存里也没有进程存在。
  • Warm:App 最近结束后再启动,有部分在内存但没有进程存在。
  • Resume:App 没结束,只是暂停,全在内存中,进程也存在。

分析阶段一般都是针对 Cold 类型进行分析,目的就是要让测试环境稳定。为了稳定测试环境有时还需要找些稳定的机型,对于 iOS 来说iPhone7性能中等,稳定性也不错就很适合,Android 的 Vivo 系列也相对稳定,华为和小米系列数据波动就比较大。除了机型外控制测试机温度也很重要,一旦温度过高系统还会降频执行影响测试数据。有时候还会置飞行模式采用 Mock 网络请求的方式来减少不稳定的网络影响测试数据。最好时重启后退 iCloud 账号,放置一段时间再测,更加准确些。

了解启动的阶段目的就是聚焦范围,从用户体验上来确定哪个阶段要快,以便能够让用户可视和响应用户操作的时间更快。

简单来说 iOS 启动过程是:

  • 根据 info.plist 里的设置加载闪屏,建立沙箱,对权限进行检查等
  • 加载 Mach-O
  • 执行 attribute 的 constructor 函数
  • 加载类扩展
  • 加载 C++静态对象
  • 调用+load 函数
  • 执行 main 函数
  • Application 初始化,到 applicationDidFinishLaunchingWithOptions 执行完
  • 初始化帧渲染,到 viewDidAppear 执行完,用户可见可操作。

也就是说对启动阶段的分析以 viewDidAppear 为截止。这次优化之前已经对 Application 初始化之前做过优化,效果并不明显,没有本质的提高,所以这次主要针对 Application 初始化到 viewDidAppear 这个阶段各个性能多纬度进行分析。

工具的选择其实目前看来是很多的,Xcode11开始 XCTest 还提供了测量性能的 Api。苹果在2019年 WWDC 启动优化专题 Optimizing App Launch - WWDC 2019 - Videos - Apple Developer 上也介绍了 Instruments 里的最新模板 App launch 如何分析启动性能。但是要想达到对启动数据进行留存取均值、Diff、过滤、关联分析等自动化操作,App launch 目前还没法做到。

主线程耗时

多个维度性能纬度分析中最重要,最终用户体感到的是主线程耗时分析。对主线程方法耗时可以直接使用Messier - 简单易用的Objective-C方法跟踪工具 - everettjf - 首先很有趣
生成 trace json 进行分析,或者参看这个代码GCDFetchFeed/SMCallTraceCore.c at master · ming1016/GCDFetchFeed · GitHub,自己手动 hook objc_msgSend 生成一份Objective-C 方法耗时数据进行分析。还有种插桩方式,可以解析 IR(加快编译速度),然后在每个方法前后插入耗时统计函数。文章后面我会着重介绍如何开发工具进一步分析这份数据,以达到监控启动阶段方法耗时的目的。

阅读全文

 发表评论


表情