抖音研发实践:基于二进制文件重排的解决方案 APP启动速度提升超15%

背景

启动是App给用户的第一印象,对用户体验至关重要。抖音的业务迭代迅速,如果放任不管,启动速度会一点点劣化。为此抖音iOS客户端团队做了大量优化工作,除了传统的修改业务代码方式,我们还做了些开拓性的探索,发现修改代码在二进制文件的布局可以提高启动性能,方案落地后在抖音上启动速度提高了约15%。

本文从原理出发,介绍了我们是如何通过静态扫描和运行时trace找到启动时候调用的函数,然后修改编译参数完成二进制文件的重新排布。

原理

Page Fault

进程如果能直接访问物理内存无疑是很不安全的,所以操作系统在物理内存的上又建立了一层虚拟内存。为了提高效率和方便管理,又对虚拟内存和物理内存又进行分页(Page)。当进程访问一个虚拟内存Page而对应的物理内存却不存在时,会触发一次缺页中断(Page Fault),分配物理内存,有需要的话会从磁盘mmap读人数据。

通过App Store渠道分发的App,Page Fault还会进行签名验证,所以一次Page Fault的耗时比想象的要多:

Page Fault

重排

编译器在生成二进制代码的时候,默认按照链接的Object File(.o)顺序写文件,按照Object File内部的函数顺序写函数。

静态库文件.a就是一组.o文件的ar包,可以用ar -t查看.a包含的所有.o。

默认布局

简化问题:假设我们只有两个page:page1/page2,其中绿色的method1和method3启动时候需要调用,为了执行对应的代码,系统必须进行两个Page Fault。

但如果我们把method1和method3排布到一起,那么只需要一个Page Fault即可,这就是二进制文件重排的核心原理。

重排之后

我们的经验是优化一个Page Fault,启动速度提升0.6~0.8ms。

核心问题

为了完成重排,有以下几个问题要解决:

  • 重排效果怎么样 - 获取启动阶段的page fault次数

  • 重排成功了没 - 拿到当前二进制的函数布局

  • 如何重排 - 让链接器按照指定顺序生成Mach-O

  • 重排的内容 - 获取启动时候用到的函数


System Trace

日常开发中性能分析是用最多的工具无疑是Time Profiler,但Time Profiler是基于采样的,并且只能统计线程实际在运行的时间,而发生Page Fault的时候线程是被blocked,所以我们需要用一个不常用但功能却很强大的工具:System Trace。

选中主线程,在VM Activity中的File Backed Page In次数就是Page Fault次数,并且双击还能按时序看到引起Page Fault的堆栈:

System Trace

signpost

现在我们在Instrument中已经能拿到某个时间段的Page In次数,那么如何和启动映射起来呢?

我们的答案是:os_signpost

os_signpost是iOS 12开始引入的一组API,可以在Instruments绘制一个时间段,代码也很简单:

1os_log_t logger = os_log_create("com.bytedance.tiktok", "performance");
2os_signpost_id_t signPostId = os_signpost_id_make_with_pointer(logger,sign);
3//标记时间段开始
4os_signpost_interval_begin(logger, signPostId, "Launch","%{public
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值