手淘架构组最新实践 | iOS基于静态库插桩的⼆进制重排启动优化
作者|谢俊逸(极目)
出品|阿里巴巴新零售淘系技术部
本文知识点提炼:
1、APP 启动时 PageFault 的性能分析
2、静态库插桩重排方案的技术原理
背景
近期抖音和Facebook分享了自己通过二进制重排优化启动时间的方案,手淘iOS架构团队也对二进制重排进行了研究,由于手淘工程模块已经二进制化,因此实现了一套基于静态库插桩的重排方案。
APP 启动 和 PageFault
当我们向操作系统申请内存时,操作系统并不是直接分配给我们物理内存,而是只标记当前进程拥有该段内存,当真正使用这段段内存时才会分配。这种延迟分配物理内存的方式就通过page fault机制来实现的。当我们访问一个内存地址时,如果该地址非法,或者我们对其没有访问权限,或者该地址对应的物理内存还未分配,cpu都会生成一个page fault,进而执行操作系统的page fault handler。如果是因为还未分配物理内存,操作系统会立即分配物理内存给当前进程,然后重试产生这个page fault的内存访问指令。
App在启动时,需要执行各种函数,我们需要读取TEXT段代码到物理内存中,这个过程会发生缺⻚中断,由于启动时所需要执行的代码分布在TEXT段的各个部分,会读取很多⻚面,导致启动时Page Fault 数量非常多。与直接访问物理内存不同,page fault过程大部分是由软件完成的,消耗时间比较久,所以是影响启动性能的一个关键指标。
例如下图中,手淘启动时首先的调用的几个方法 会分布在虚拟内存的各个⻚面中, 执行这些方法时,需要从读取到物理内容中,就会产生多次page fault。
如果能将启动阶段需要的读取代码集中排布,将这些方法全都放到相邻的区域中,我们读取这些方法可能就只需要极少的page fault次数。可以减少不必要的page fault时间。达到优化启动时间的效果。
重排前后的函数在页面的布局对比:
重排方案
如何获取方法的执行顺序
为了生成order_file, 我们需要确定应用启动时方法的执行顺序。之前抖音和facebook都分享过自己的方案,在实际操作的过程中,我们发现抖音和 facebook 的方案并不适用于手淘。
抖音通过静态扫描和运行时Trace等方法确定 order_file,该方案无法覆盖 initialize、block 和 C++通过寄存器的间接函数调用静态扫描不出来调用。
facebook 分享过通过 llvm 插桩的确定 order_file 的方案,需要使用源码重新打包。由于手淘几乎全是已经编译好的二进制模块,在手淘使用该方案不现实。
只能想其他办法...
手淘之前已经做过pod预编译,我和师兄念纪想到了是否可以通过在汇编层面对pod编译后的静态库进行插桩。在启动时,插桩后的方法都会调用记录方法,从而获得启动方法的执行顺序。在参考了离青对汇编插桩的研究后,确定了静态库插桩的实现方案。
静态库插桩
我们编译过的静态库由.o文件组成,我们可以对.o中的函数代码进行修改,在每个函数的开头插入调用我们指定记录函数的指令。
举个例子:
插入前-[MyApp window]:的汇编代码
-[MyApp window]:
0000000000002d88 adrp x8, #0x
0000000000002d8c ldrsw x8, [x8, #0xf18]
; 0x2f18@PAGEOFF, _OBJC_IVAR_$_MyApp._window
0000000000002d90 ldr x0, [x0, x8]
0000000000002d94 ret
插入后的 汇编代码,可以看到 增加了跳转到_record_method的指令,并且补上了prologue和
epilogue。
-[MyApp window]:
0000000000002ebc stp x29, x30, [sp, #-0x10]!
0000000000002ec0 mov x29, sp
0000000000002ec4 bl _record_method
0000000000002ec8 ldp x29, x30, [sp], #0x
0000000000002ecc adrp x8, #0x
0000000000002ed0 ldrsw x8, [x8, #0xc0]
0000000000002ed4 ldr x0, [x0, x8]
0000000000002ed8 ret
生成order file
linkmap记录了连接过程中的相关信息。其中包含链接用到的symbol相关的信息。通过pc address减去slide得到的地址,我们可以在linkmap中找到对应的symbol.
address = pc - slide. // 因为ASLR, APP 可执行文件随机载入的原因,需要处理一下偏移
量。
我们需要将之前记录的地址转换成对应的符号,为了真实还原线上的执行环境,我们只是在app中简单地的记录了 pc地址 和 Image的偏移量。通过解析linkmap,获取函数的地址区间, 得到距离address最近的symbol,生成order_file。
linkmap 文件:
# Symbols:
# Address Size File Name
0x100001630 0x00000039 [ 2] -[ViewController viewDidLoad]
0x100001670 0x00000092 [ 3] _main
0x100001710 0x00000080 [ 4] -[AppDelegate application:didFinishLaunchingWithOptions:]
0x100001790 0x00000040 [ 4] -[AppDelegate applicationWillResignActive:]
0x1000017D0 0x00000040 [ 4] -[AppDelegate applicationDidEnterBackground:]
0x100001810 0x00000040 [ 4] -[AppDelegate applicationWillEnterForeground:]
0x100001850 0x00000040 [ 4] -[AppDelegate applicationDidBecomeActive:]
0x100001890 0x00000040 [ 4] -[AppDelegate applicationWillTerminate:]
更改符号的排列顺序
默认情况下,ld链接器会按照链接的顺序将各个.o文件的数据重新布局生成可执行文件。ld链接器提供-order-file选项操控数据排列的顺序。在Xcode中可以通过Order File选项指定符号排序文件。
//Order file 内容例子:
+[xxxxx1 load]
+[xxxxx2 swizzleResumeAndSuspendMethodForClass:]
+[xxxxx3 load]
+[xxxxx4 initialize]___
+[xxxxx5 initialize]_block_invoke
+[xxxxx6 initialize]___
+[xxxxx7 initialize]_block_invoke
...
优化效果
通过精准的启动函数重排,最后重排效果还是很可观的,在iPhone6上优化了400ms的启动时间。
参考
感谢抖音团队和Facebook团队提供优化新思路
抖音研发实践:基于二进制文件重排的解决方案 APP启动速度提升超15%https://mp.weixin.qq.com/s/Drmmx5JtjG3UtTFksL6Q8Q
Improving iOS Startup Performance with Binary Layout Optimizations
https://atscaleconference.com/videos/performance-scale-improving-ios-startup-performance-with-binary-
layout-optimizations/
Linux下Page Fault的处理流程 https://cloud.tencent.com/developer/article/1459526
We are hiring
淘宝基础平台团队正在进行社招招聘,岗位有iOS Android客户端开发工程师、Java研发工程师、C/C++研发工程师、前端开发工程师、算法工程师,欢迎投递简历至📮:junzhan.yzw@taobao.com
如果你想更详细了解淘宝基础平台团队,点击下方“阅读原文”观看团队介绍视频
更多淘宝基础平台团队的技术分享,可关注淘系技术微信公众号AlibabaMTT

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
阿里云助力印尼实现“数字化愿景”,开拓互联网掘金之路
1月16日,阿里云峰会首次来到了印度尼西亚首都雅加达,一同见证了印尼的数字化转型过程和取得的丰厚硕果。 作为亚太区市场份额第一,并且也是唯一一家在印尼拥有数据中心的全球化公共云服务商,阿里云一直致力于帮助印尼企业实现“数字化愿景”,共享云上福利。 2018年3月,阿里云印度尼西亚大区正式开服运营,成为该国第一家在当地运营的国际公共云服务商。2019年年初,又开设了第二个数据中心。耕耘几年,阿里云已经成为印尼非常领先的云计算厂商,客户覆盖互联网、金融、物流、零售等多个行业。 在发布会现场,阿里巴巴集团副总裁、阿里云数据库产品事业部总裁李飞飞为大家介绍了阿里云在下一代企业级数据库系统上的技术创新与实践。 1、完整生态,领跑中国云数据库时代 阿里云拥有国内最强大和丰富的云数据库产品家族,涵盖关系型数据库、非关系型数据库、分析型数据库及迁移工具等,
-
下一篇
Tablestore入门指南-GetRange范围查询详解
查询接口 表格存储Tablestore作为大数据存储服务,提供了多种数据输出接口,主要包含:单行读(GetRow)、 批量读(BatchGetRow)、 范围读(GetRange)、多元索引检索(Search)以及通道服务的数据订阅(Tunnel Service)。本文将详细讲述范围读的功能、使用与限制。 功能说明 GetRange接口提供了Tablestore数据的范围读取能力。通过接口,范围内数据将会按照指定的顺序(正序或逆序)逐页返回。用户需要提供范围的起始、终止的主键,来限定数据的范围。若总数据未能一个请求完整返回,可以通过连续翻页的方式,持续遍历下一页,知道获取完整结果。 限制条件 一次返回数据的行数超过 5000 行,或者返回数据的数据大小大于 4 MB。满足以上任一条件时,超出上限的数据将会按行级别被截掉并返回下一行数据主键信息。 API定义 message GetRangeRequest { required string table_name = 1; // 表名 required Direction direction = 2; // 返回顺序 repeated s...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- MySQL数据库在高并发下的优化方案
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Docker容器配置,解决镜像无法拉取问题
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- 2048小游戏-低调大师作品
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作