二进制重排(三)

135 阅读2分钟

二进制重排

二进制重排为何能优化启动速度?主要跟缺页中断page fault有关, 因为我们的app在冷启动的时候有大量的类和大量的函数,它需要被加载和执行,这个时候产生的page fault所带来的时间消耗是比较大的,虽然一次page fault带来的时间消耗是毫秒级别,但是如果有几百个几千个这样的消耗,那这个时间加起来的消耗就很多。

一、缺页分析

首先打开自己的app,启动跑到真机上,然后按cmd + ctrl + i启动Instruments

Pasted Graphic.png 选择System Trace 点击Choose,打开后点击箭头指向位置:

Pasted Graphic 1.png

就会开始分析启动情况:

Pasted Graphic 2.png

分析完毕,再次点击箭头位置结束:

Pasted Graphic 3.png

在搜索框输入main

Pasted Graphic 4.png

Narrative选择 Summary: Virtual Memory

Pasted Graphic 6.png

点击要优化app的Main Thread,可以看到File Backed Page In 这个就是page fault 缺页异常,总共是2200个,冷启动总耗时是441ms,占总耗时454ms非常大的比例

Pasted Graphic 5.png

这个地方就可以去优化,去减少page fault的次数

二、启动分析

把工程点击Build Settings,搜素link map,把Wirte Link Map File 改为Yes

Pasted Graphic 9.png

cmd + b编译一下,点击Product,选择show Buld Folder In Finder

按照顺序打开 Intermediates.noindex —> app名称.build —> Debug-iphoneos —>  app名称.build  —> app名称-LinkMap-normal-arm64.txt

Pasted Graphic 11.png

用Xcode打开,搜索# Address可以看到函数名称和地址:

1__#$!@%!#__Pasted Graphic 5.png

这里是编译过后的地址,虽然是虚拟内存的地址,还没有加上ASLR,还需要加上一步rebase之后,才是真的虚拟内存的地址。这里的地址相当于第二篇说的P2P3

1__#$!@%!#__Pasted Graphic.png

而函数的排列,这是代码的编译顺序:

1__#$!@%!#__Pasted Graphic 6.png

如果在排列这些方法的时候,app所有启动需要调用到的数据都排列在最前面得page位置,这样就能最大程度的启动时候的page加载情况,并且减少大大减少page fault缺页中断,最大程度的优化启动速度,这个行为就叫二进制重排,即重新排列程序的二进制文件

三、启动顺序调整

这个时候就需要一个.order文件,给编译器使用,这里先简单用一个例子举例:

1__#$!@%!#__Pasted Graphic 1.png

Demo1 目录下的终端输入:

touch jt.order

打开 jt.order

输入:

_main
_66666666666666
+[AppDelegate load]
+[ViewController load]

1__#$!@%!#__Pasted Graphic 2.png

关闭保存

Bulid Settings 输入 order 选择Order File 添加./jt.order

1__#$!@%!#__Pasted Graphic 3.png

cmd + b编译一下,点击Product,选择show Buld Folder In Finder

1__#$!@%!#__Pasted Graphic 4.png

还是打开 Intermediates.noindex —> app名称.build —> Debug-iphoneos —>  app名称.build  —> app名称-LinkMap-normal-arm64.txt

可以看到,现在编译的顺序就是我们.order文件里面写的顺序,而且无关输入被忽略,这样就可以根据我们设置方法的启动顺序

总结

通过.order文件把启动阶段所需要的符号都放在前面几个page里面,这样我们在启动的时候所需要加载的page的页数就减少了,相应page fault的次数就减少了,启动速度就相应加快,那怎样才能知道一个程序启动他需要哪些函数,哪些函数这就需要用到clang插桩。