189 8069 5689

高德APP启动耗时剖析与优化实践(iOS篇)-创新互联

前言
最近高德地图APP完成了一次启动优化专项,超预期将双端启动的耗时都降低了65%以上,iOS在iPhone7上速度达到了400毫秒以内。就像产品们用后说的,快到不习惯。算一下每天为用户省下的时间,还是蛮有成就感的,本文做个小结。

主要从事网页设计、PC网站建设(电脑版网站建设)、wap网站建设(手机版网站建设)、自适应网站建设、程序开发、微网站、微信小程序等,凭借多年来在互联网的打拼,我们在互联网网站建设行业积累了丰富的网站建设、网站设计、网络营销经验,集策划、开发、设计、营销、管理等多方位专业化运作于一体,具备承接不同规模与类型的建设项目的能力。

高德APP启动耗时剖析与优化实践(iOS篇)

高德APP启动耗时剖析与优化实践(iOS篇)

(文中配图均为多才多艺的技术哥哥手绘)

启动阶段性能多维度分析

要优化,首先要做到的是对启动阶段的各个性能纬度做分析,包括主线程耗时、CPU、内存、I/O、网络。这样才能更加全面的掌握启动阶段的开销,找出不合理的方法调用。

启动越快,更多的方法调用就应该做成按需执行,将启动压力分摊,只留下那些启动后方法都会依赖的方法和库的初始化,比如网络库、Crash库等。而剩下那些需要预加载的功能可以放到启动阶段后再执行。

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

启动类型分为:

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

分析阶段一般都是针对Cold类型进行分析,目的就是要让测试环境稳定。为了稳定测试环境,有时还需要找些稳定的机型,对于iOS来说iPhone7性能中等,稳定性也不错就很适合,Android的Vivo系列也相对稳定,华为和小米系列数据波动就比较大。

除了机型外,控制测试机温度也很重要,一旦温度过高系统还会降频执行,影响测试数据。有时候还会设置飞行模式采用Mock网络请求的方式来减少不稳定的网络影响测试数据。最好是重启后退iCloud账号,放置一段时间再测,更加准确些。

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

简单来说iOS启动分为加载Mach-O和运行时初始化过程,加载Mach-O会先判断加载的文件是不是Mach-O,通过文件第一个字节,也叫魔数来判断,当是下面四种时可以判定是Mach-O文件:

  • 0xfeedface对应的loader.h里的宏是MH_MAGIC
  • 0xfeedfact宏是MH_MAGIC_64
  • NXSwapInt(MH_MAGIC)宏MH_GIGAM
  • NXSwapInt(MH_MAGIC_64)宏MH_GIGAM_64

Mach-O主要分为:

  • 中间对象文件(MH_OBJECT)
  • 可执行二进制(MH_EXECUTE)
  • VM 共享库文件(MH_FVMLIB)
  • Crash 产生的Core文件(MH_CORE)
  • preload(MH_PRELOAD)
  • 动态共享库(MH_DYLIB)
  • 动态链接器(MH_DYLINKER)
  • 静态链接文件(MH_DYLIB_STUB)符号文件和调试信息(MH_DSYM)这几种。

确定是Mach-O后,内核会fork一个进程,execve开始加载。检查Mach-O Header。随后加载dyld和程序到Load Command地址空间。通过 dyld_stub_binder开始执行dyld,dyld会进行rebase、binding、lazy binding、导出符号,也可以通过DYLD_INSERT_LIBRARIES进行hook。

dyld_stub_binder给偏移量到dyld解释特殊字节码Segment中,也就是真实地址,把真实地址写入到la_symbol_ptr里,跳转时通过stub的jump指令跳转到真实地址。dyld加载所有依赖库,将动态库导出的trie结构符号执行符号绑定,也就是non lazybinding,绑定解析其他模块功能和数据引用过程,就是导入符号。

Trie也叫数字树或前缀树,是一种搜索树。查找复杂度O(m),m是字符串的长度。和散列表相比,散列最差复杂度是O(N),一般都是 O(1),用 O(m)时间评估 hash。散列缺点是会分配一大块内存,内容越多所占内存越大。Trie不仅查找快,插入和删除都很快,适合存储预测性文本或自动完成词典。

为了进一步优化所占空间,可以将Trie这种树形的确定性有限自动机压缩成确定性非循环有限状态自动体(DAFSA),其空间小,做法是会压缩相同分支。

对于更大内容,还可以做更进一步的优化,比如使用字母缩减的实现技术,把原来的字符串重新解释为较长的字符串;使用单链式列表,节点设计为由符号、子节点、下一个节点来表示;将字母表数组存储为代表ASCII字母表的256位的位图。

尽管Trie对于性能会做很多优化,但是符号过多依然会增加性能消耗,对于动态库导出的符号不宜太多,尽量保持公共符号少,私有符号集丰富。这样维护起来也方便,版本兼容性也好,还能优化动态加载程序到进程的时间。

然后执行attribute的constructor函数。举个例子:

#include __attribute__((constructor))static void prepare() {
    printf("%s\n", "prepare");
}
__attribute__((destructor))static void end() {
    printf("%s\n", "end");
}void showHeader() { 
    printf("%s\n", "header");
}

网站名称:高德APP启动耗时剖析与优化实践(iOS篇)-创新互联
标题路径:http://cdxtjz.com/article/goeie.html

其他资讯