iOS 启动优化(二)二进制重排

源代码 2024-9-25 06:05:39 18 0 来自 中国
App启动分析

App启动分析App启动分为 冷启动 和 热启动

  • 冷启动:点击 App 启动前,它的历程不在体系里,须要体系新创建一个历程分配给它的情况。这是一次完备的启动过程
  • 热启动:App 在冷启动后,用户将App 退到背景,即在App的历程还在体系里的情况下,用户重新启动进入 App 的过程,这个过程做的变乱非常少,启动速率非常快。
因此,我们紧张针对 App 冷启动举行优化
一样寻常而言,App 启动时间,指的是从用户点击 App 开始,到用户看到第一个界面之间的时间,总结来说:App 的启动紧张包罗三个阶段:

  • main() 函数实行前
  • main() 函数实行后
  • 首屏渲染完成后
1、pre-main耗时检测
通过设置情况变量来统计 pre-main 的耗时
选择 `Edit Scheme` - `Arguments` - `Environment Variables`添加 name `DYLD_PRINT_STATISTICS` value : `${DEBUG_ACTIVITY_MODE}`可见,在 main() 函数实行前,体系紧张会做下面几件变乱:


  • dylib loading:加载可实行文件(App 的.o 文件的聚集),加载动态链接库
  • rebase/binding:对动态链接库举行 rebase 指针调解和 bind 符号绑定;
  • Objc setup:Objc 运行时的初始化处理,包罗 Objc 相干类的注册、category 注册、selector 唯一性查抄等;
  • initializer:初始化,包罗了实行 +load() 方法、attribute((constructor)) 修饰的函数的调用、创建 C++ 静态全局变量。
相应地,这个阶段对于启动速率优化来说,可以做的变乱包罗:


  • 镌汰动态库加载:每个库本身都有依赖关系,苹果公司发起利用更少的动态库,而且发起在利用动态库的数目较多时,只管将多个动态库举行合并。数目上,苹果公司发起最多利用 6 个非体系动态库。
  • 镌汰加载启动后不会去利用的类大概方法。
  • +load()方法里的内容可以放到首屏渲染完成后再实行,或利用 +initialize()方法更换掉。由于,在一个 +load() 方法里,举行运行时方法更换利用会带来 4 毫秒的斲丧。不要藐视这 4 毫秒,集腋成裘,实行 +load() 方法对启动速率的影响会越来越大。
  • 控制 C++ 全局变量的数目。
2.png 当我们做了以上工作,对 pre-main 的时间有所优化之后,如果还想再举行优化,那就须要利用 LLVM 为我们提供的优化方式:二进制重排
Page Fault

历程如果能直接访问物理内存无疑是很不安全的,以是利用体系在物理内存的上又创建了一层假造内存。为了提高服从和方便管理,又对假造内存和物理内存又举行分页(Page)。当历程访问一个假造内存Page而对应的物理内存却不存在时,会触发一次缺页制止(Page Fault),分配物理内存,有须要的话会从磁盘mmap读人数据。
通过App Store渠道分发的App,Page Fault还会举行署名验证,以是一次Page Fault的耗时比想象的要多

LinkMap

LinkMap 是iOS编译过程的中央产物,记载了二进制文件的结构,须要在Xcode的Build Settings里开启Write Link Map File:


Path to Link Map File:
选中编译后的 app,Show In Finder -- 找到build目次 -- 详细路径如下:
Build/Intermediates.noindex/Spirit.build/Debug-iphonesimulator/Spirit.build/Spirit-LinkMap-normal-x86_64.txt
LinkMap 紧张包罗三大部分:


  • Object Files 天生二进制用到的link单元的路径和文件编号
  • Sections 记载Mach-O每个Segment/section的地点范围
  • Symbols 按序次记载每个符号的地点范围
5.png 通过MapLink就可以看到链接的函数的序次和引用的文件序次是同等的
重排

编译器在天生二进制代码的时间,默认按照链接的Object File(.o)序次写文件,按照Object File内部的函数序次写函数。
静态库文件.a就是一组.o文件的ar包,可以用ar -t检察.a包罗的全部.o。
题目分析:假设我们只有两个 page:page1/page2,此中绿色的method1 和 method3 启动时间须要调用,为了实行对应的代码,体系必须举行两个 Page Fault。
7.png 但如果我们把 method1 和 method3 排布到一起,那么只须要一个Page Fault 即可,这就是二进制文件重排的核心原理
8.png 为了完成重排,有以下几个题目要办理:

  • 重排结果怎么样 - 获取启动阶段的page fault次数
  • 重排成功了没 - 拿到当前二进制的函数结构
  • 怎样重排 - 让链接器按照指定序次天生Mach-O
  • 重排的内容 - 获取启动时间用到的函数
获取启动阶段的page fault次数

System Trace

一样平常开辟中性能分析是用最多的工具无疑是Time Profiler,但Time Profiler是基于采样的,而且只能统计线程实际在运行的时间,而发生Page Fault的时间线程是被blocked,以是我们须要用一个不常用但功能却很强大的工具:System Trace
选中主线程,在VM Activity中的File Backed Page In次数就是Page Fault次数,而且双击还能按时序看到引起Page Fault的堆栈:
File Backed Page In 即为 Page Fault 的个数
拿到当前二进制的函数结构

可以通过Map Link 获取到当前的二进制函数结构表
让链接器按照指定序次天生Mach-O

ld

Xcode 利用的链接器件是ld,ld有一个不常用的参数 -order_file,通过man ld可以看到详细文档:
Alters the order in which functions and data are laid out.For each section in the output file, any symbol in that sec-tion that are specified in the order file file is moved to the start of its section and laid out in the same order as in the order file file.  Order files are text files with one symbol name per line.  Lines starting with a # are comments. A symbol name may be optionally preceded with its object file leaf name and a colon (e.g. foo.o:_foo).  This is useful for static functions/data that occur in multiple files.  A symbol name may also be optionally preceded with the architecture (e.g. ppc:_foo or ppc:foo.o:_foo).  This enables you to have one order file that works for multiple architectures.  Lit-eral c-strings may be ordered by by quoting the string (e.g."Hello, world\n") in the order file.
可以看到,order_file中的符号会按照序次分列在对应section的开始,完善的满足了我们的需求。
Xcode的GUI也提供了order_file选项

Xcode 的毗连器 ld 默认忽略 order file 中 不存在的方法
如果在 Other Linker Flags: Debug 中添加-order_file_statistics,会以 warning 的情势把这些没找到的符号打印在日志里
获取启动调用的函数符号

Clang官方文档

  • LLVM支持我们在添加编译选项 -fsanitize-coverage=trace-pc-guard 的时间,编译时帮我们在函数中插入__sanitizer_cov_trace_pc_guard,当函数调用的时间,会callq__sanitizer_cov_trace_pc_guard
  • 利用 __builtin_return_address(0) 来获得当前函数返回地点,也就是调用方的地点。
  • 通过 dladdr 来将指针分析成 Dl_info 结构体信息,此中dli_sname 就是符号的名称
简单来说 SanitizerCoverage 是 Clang 内置的一个代码覆盖工具。它把一系列以 __sanitizer_cov_trace_pc_ 为前缀的函数调用插入到用户界说的函数里,借此实现了全局 AOP 的大杀器。其覆盖之广,包罗 Swift/Objective-C/C/C++ 等语言,Method/Function/Block 全支持。
Build Settings:
在App 的 Target - Build Settings - Other C Flags Debug 添加 -fsanitize-coverage=func,trace-pc-guard
OC - Swift 混编,则在 Other Swift Flags Debug 添加 -sanitize-coverage=func 和 -sanitize=undefined
Cocoapods 管理的第三方库 可以通过Pod提供的hook来修改全部的pod库的编译选项
代码如下: 在hock方法 post_install 内里做修改设置的利用
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 18:23, Processed in 0.154304 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表