iOS冷启动速度提升50%

 随着项目的不停迭代复杂,app的启动时间越来越慢,影响了用户体验。所以,我们决定进行一次app的冷启动优化,减少用户的启动等待时间,提升用户体验。
用户点击APP到首页加载完成经历了哪些阶段,知道了启动阶段做了什么事情,才知道如何去找出痛点


image.png

点击APP后
1.系统exec()为程序开启一段内存空间
2.加载可执行文件(Mach-o)
3.加载动态库
4.rebase:因为ASDL技术,每次启动内存地址随机化,所以需要根据偏移量进行变基修正
5.bind:将指针正确指向镜像外部的内容
6.Objc setup

  • 注册OC类
  • 分类列表插入方法列表
  • 保证selector唯一
  1. initializers
  • load方法
  • c++构造函数
  • c++静态全局变量创建
    然后就是执行main函数,main会创建一个applicationdelegate,默认创建一个主Runloop,然后执行didfinishlauch方法,最后是首页的渲染。

 问题:如何量化耗时操作?
 Xcode集成的Profile和火焰图可以查看各个阶段的耗时,定位到具体哪些操作拖慢了了冷启动。由于Profile的使用教程网上一大堆,这里就不做详述。
 笔者通过Profile分析冷启动过程,发现了最耗时的操作大致是以下几点:
 1.main函数到didFinishLaunchingWithOptions阶段集中了大量的sdk初始化,配置和各种数据的初始化。
 2.执行了大量+load()方法
 3.首页一些比较隐晦的耗时操作
 4.page Fault过多,可以使用二进制重排

解决方案

1.二进制重排

成效:

Page fault: 5639->5035 ,时间从1.3s->666.20ms,收益还是很大的。

image.png

原理:

 计算机是为了提高内存使用效率和安全性,使用了虚拟内存和分页。当进程访问一个page的时候,在DRAM缓存没有命中会触发缺页(Page fault),编译器在生成机器码的过程中,会按照链接的顺序进行写文件和函数。假如启动时有2个方法并且分别在page1和page2,那么就需要触发2次缺页。如果将这2个方法都写在page1,那么就会减少page2的中断。就减少了一次中断。综上所述:二进制重排的核心思想就是减少Page fault的次数

实现步骤和遇到的问题:

 二进制重排的大致思路是hook项目里的所有方法,然后收集所有启动项的方法写入.order文件里,在Xcode里指定order File路径为创建的.order文件。
 参考业界比较成熟的方案可以采用静态插桩收集启动函数。代码我就不写了,网上一大堆。要提一句的是在静态插装的使用过程中有遇到一个问题。
笔者项目有对NSDictionary 的setvalue forkey等系统方法全局hook进行防崩溃处理。在收集函数时,发现setvalue会循环调用,后来查看LLVM官方文档发现需要在__sanitizer_cov_trace_pc_guard函数里手动*guard = 0;官方有给详细解释,请看下图

image.png

__sanitizer_cov_trace_pc_guard可能会被多次调用,当设置*guard = 0时候回调函数只会执行1次,就解决了这个问题。

疑问1:如果后续业务迭代,没有进行二进制重排,会对新增的业务产生影响吗?(比如在didfinishLauch方法了新增业务逻辑会去执行吗?)

不会,笔者写了个demo去验证。


image.png

删除order文件里的func1


image.png

clean项目,然后rebuild项目,得到的Link map File如下
image.png

func1方法也正常invoke了。


image.png
2.路由后置

在启动项的initializers阶段如果使用大量load方法也会拖慢冷启动速度。
笔者项目里页面使用路由跳转,经过统计项目里达到300+的load方法调用。
思路:在编译的时候,通过attribute((section()))将方法注册到指定输入段中,在启动后某个时机一起进行注册
详情请看demo。

3.main函数到首页渲染过程中业务逻辑的梳理

 1、根据业务的优先级与紧急程度整理初始化配置项的加载顺序
根据我们APP的业务场景,大致分为了三个优先级等级(A、B、C,优先级依次降低)

优先级A:一些防止越界保护的分类,环境设置等,app一开始就需要的
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions

image.png

优先级B:必要的正常业务,比如定位,埋点,热修复的初始化等
优先级C:比如城市数据库加载等,首页viewDidAppear方法渲染完成以后开始加载

 2、整理首页UI初始化和加载逻辑,将一些耗时的操作初始化懒加载,可以后置的业务尽量后置

 经过这3个方面的优化,App的启动速度大概提升了50%左右。

你可能感兴趣的:(iOS冷启动速度提升50%)