二进制重排
二进制重排为何能优化启动速度?主要跟缺页中断page fault有关, 因为我们的app在冷启动的时候有大量的类和大量的函数,它需要被加载和执行,这个时候产生的page fault所带来的时间消耗是比较大的,虽然一次page fault带来的时间消耗是毫秒级别,但是如果有几百个几千个这样的消耗,那这个时间加起来的消耗就很多。
一、缺页分析
首先打开自己的app,启动跑到真机上,然后按cmd + ctrl + i
,启动Instruments
:
选择
System Trace
点击Choose
,打开后点击箭头指向位置:
就会开始分析启动情况:
分析完毕,再次点击箭头位置结束:
在搜索框输入main
:
Narrative
选择 Summary
: Virtual Memory
:
点击要优化app的Main Thread
,可以看到File Backed Page In
这个就是page fault
缺页异常,总共是2200
个,冷启动总耗时是441ms
,占总耗时454ms
非常大的比例
这个地方就可以去优化,去减少page fault
的次数
二、启动分析
把工程点击Build Settings
,搜素link map
,把Wirte Link Map File
改为Yes
按cmd + b
编译一下,点击Product
,选择show Buld Folder In Finder
按照顺序打开
Intermediates.noindex
—>app名称.build
—>Debug-iphoneos
—>app名称.build
—>app名称-LinkMap-normal-arm64.txt
用Xcode打开,搜索# Address
可以看到函数名称和地址:
这里是编译过后的地址,虽然是虚拟内存的地址,还没有加上ASLR
,还需要加上一步rebase
之后,才是真的虚拟内存的地址。这里的地址相当于第二篇说的P2
、P3
而函数的排列,这是代码的编译顺序:
如果在排列这些方法的时候,app所有启动需要调用到的数据都排列在最前面得page
位置,这样就能最大程度的启动时候的page
加载情况,并且减少大大减少page fault
缺页中断,最大程度的优化启动速度,这个行为就叫二进制重排,即重新排列程序的二进制文件
三、启动顺序调整
这个时候就需要一个.order
文件,给编译器使用,这里先简单用一个例子举例:
在Demo1
目录下的终端输入:
touch jt.order
打开 jt.order
输入:
_main
_66666666666666
+[AppDelegate load]
+[ViewController load]
关闭保存
在Bulid Settings
输入 order
选择Order File
添加./jt.order
:
按cmd + b
编译一下,点击Product
,选择show Buld Folder In Finder
还是打开
Intermediates.noindex
—>app名称.build
—>Debug-iphoneos
—>app名称.build
—>app名称-LinkMap-normal-arm64.txt
可以看到,现在编译的顺序就是我们.order
文件里面写的顺序,而且无关输入被忽略,这样就可以根据我们设置方法的启动顺序
总结
通过.order
文件把启动阶段所需要的符号都放在前面几个page
里面,这样我们在启动的时候所需要加载的page的页数就减少了,相应page fault
的次数就减少了,启动速度就相应加快,那怎样才能知道一个程序启动他需要哪些函数,哪些函数这就需要用到clang
插桩。