iOS 啟動(dòng)優(yōu)化②之二進(jìn)制重排

虛擬內(nèi)存

????在了解二進(jìn)制重排之前,我們先了解虛擬內(nèi)存趟薄,詳細(xì)的可以查看iOS 系統(tǒng)是怎么管理內(nèi)存的
????電腦中所運(yùn)行的程序均需經(jīng)由內(nèi)存執(zhí)行荷并,若執(zhí)行的程序占用內(nèi)存很大或很多赏枚,則會(huì)導(dǎo)致內(nèi)存消耗殆盡。為解決該問題脓规,Windows 中運(yùn)用了虛擬內(nèi)存技術(shù)栽连,即勻出一部分硬盤空間來(lái)充當(dāng)內(nèi)存使用。主要用于解決當(dāng)多個(gè)進(jìn)程同時(shí)存在時(shí),對(duì)物理內(nèi)存的管理秒紧。提高了CPU的利用率绢陌,使多個(gè)進(jìn)程可以同時(shí)、按需加載熔恢。所以脐湾,虛擬內(nèi)存其本質(zhì)就是一張?zhí)摂M地址和物理地址對(duì)應(yīng)關(guān)系的映射表

Page Fault

????在虛擬內(nèi)存部分叙淌,當(dāng)進(jìn)程訪問一個(gè)虛擬內(nèi)存page秤掌,而對(duì)應(yīng)的物理內(nèi)存不存在時(shí),會(huì)觸發(fā)缺頁(yè)中斷(Page Fault)鹰霍,因此阻塞進(jìn)程闻鉴。此時(shí)就需要先加載數(shù)據(jù)到物理內(nèi)存,然后再繼續(xù)訪問衅谷。這個(gè)對(duì)性能是有一定影響的椒拗。基于Page Fault获黔,App在冷啟動(dòng)過程中蚀苛,會(huì)有大量的類、分類玷氏、三方等需要加載和執(zhí)行堵未,此時(shí)的產(chǎn)生的 Page Fault 所帶來(lái)的的耗時(shí)是很大的。

二進(jìn)制重排原理

????編譯器在生成二進(jìn)制代碼的時(shí)候盏触,默認(rèn)按照鏈接的 Object File(.o) 順序(也就是 Targets->Build Phases->Compile Sources 中的文件順序)寫文件渗蟹,按照 Object File 內(nèi)部的函數(shù)順序?qū)懞瘮?shù)。

靜態(tài)庫(kù)文件.a就是一組.o文件的ar包赞辩,可以用ar -t查看.a包含的所有.o雌芽。

????如下圖,page1 和 page2辨嗽,其中 method1method3 啟動(dòng)時(shí)候需要調(diào)用世落,為了執(zhí)行對(duì)應(yīng)的代碼,系統(tǒng)必須進(jìn)行兩次Page Fault糟需。

默認(rèn)

????但如果把 method1method3 排布到一起屉佳,那么只需要一個(gè) Page Fault 即可,這就是二進(jìn)制文件重排的核心原理: 將所有啟動(dòng)時(shí)刻需要調(diào)用的方法排列在一起
重排

獲取啟動(dòng)階段的 page fault 次數(shù)

通過 System Trace 拿到某個(gè)時(shí)間段的 page fault 次數(shù)

????日常開發(fā)中性能分析是用最多的工具無(wú)疑是Time Profiler洲押,但Time Profiler是基于采樣的武花,并且只能統(tǒng)計(jì)線程實(shí)際在運(yùn)行的時(shí)間,而發(fā)生Page Fault的時(shí)候線程是被blocked杈帐,所以我們需要用一個(gè)不常用但功能卻很強(qiáng)大的工具:System Trace体箕。

Product-profile-SystemTrace

????選中主線程,在 VM Activity 中的 File Backed Page In 次數(shù)就是 Page Fault 次數(shù),并且雙擊還能按時(shí)序看到引起 Page Fault 的堆棧:
查看 Main Thread 中的File Backed Page In

通過 LinkMap 拿到當(dāng)前二進(jìn)制的函數(shù)布局

????LinkMap 是 iOS 編譯過程的中間產(chǎn)物干旁,記錄了二進(jìn)制文件的布局驶沼,需要將 Xcode 的 Build Settings -> Write Link Map File 設(shè)置為 YES
linkmap主要包括三大部分:

  • Object Files 生成二進(jìn)制用到的 link 單元的路徑和文件編號(hào)

  • Sections 記錄 Mach-O 每個(gè) Segment/section 的地址范圍

  • Symbols 按順序記錄每個(gè)符號(hào)的地址范圍

????通過 Link map 文件的查看,我們可以看到 在 Symbols 中有著二進(jìn)制的函數(shù)布局争群。

Link map 的 Symbols

通過 Order File 讓鏈接器按照指定順序生成 Mach-O

????Xcode 使用的鏈接器件是 ld ,ld 有一個(gè)不常用的參數(shù) -order_file大年,我們可以通過在 Build Settings -> Order File 配置一個(gè)后綴為 .order 的文件路徑换薄。 通過 Order File 我們可以更改函數(shù)和數(shù)據(jù)布局的順序。當(dāng)然翔试,我們最好不要在調(diào)試或開發(fā)配置中指定 Order File轻要,因?yàn)檫@會(huì)使鏈接的二進(jìn)制文件對(duì)調(diào)試器的可讀性降低。僅在發(fā)布時(shí)使用 Order File 垦缅。

不需要擔(dān)心 Order File 中的符號(hào)是不存在的冲泥,因?yàn)?ld 會(huì)忽略這些符號(hào)

Build Settings -> Order File

那么,如何編寫自己的 .order 文件呢壁涎?可以參考下面的示例:

test //函數(shù)
[ViewController orderTest]//方法

當(dāng)我們編寫完 Order File 文件后凡恍,重新編譯工程就可以在 LinkMap 文件中查看到已經(jīng)調(diào)整后的二進(jìn)制函數(shù)布局

Clang 插樁

還剩下最后一個(gè),也是最核心的一個(gè)問題怔球,獲取啟動(dòng)時(shí)候用到的函數(shù)符號(hào)嚼酝。LLVM 內(nèi)置了一個(gè)簡(jiǎn)單的代碼覆蓋率檢測(cè) SanitizerCoverage。它在函數(shù)級(jí)竟坛、基本塊級(jí)和邊緣級(jí)插入對(duì)用戶定義函數(shù)的調(diào)用闽巩。提供了這些回調(diào)的默認(rèn)實(shí)現(xiàn),并實(shí)現(xiàn)了簡(jiǎn)單的覆蓋率報(bào)告和可視化担汤。

配置 SanitizerCoverage

  • 工程 Target 配置
    Targets->Build Settings -> Other C Flags 中添加 -fsanitize-coverage=func,trace-pc-guard

    -fsanitize-coverage=func,trace-pc-guard

  • Podfile 配置

    post_install do |installer|
      installer.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
          config.build_settings['OTHER_CFLAGS'] = '-fsanitize-coverage=func,trace-pc-guard'
        end
      end
    end
    

實(shí)現(xiàn) SanitizerCoverage 的方法

實(shí)現(xiàn) __sanitizer_cov_trace_pc_guard_init__sanitizer_cov_trace_pc_guard 方法

void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,
                                                    uint32_t *stop) {
  static uint64_t N;  // Counter for the guards.
  if (start == stop || *start) return;  // Initialize only once.
  printf("INIT: %p %p\n", start, stop);

}

void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
   if (!*guard) return;  // Duplicate the guard check.
   void *PC = __builtin_return_address(0);
   printf("guard: %p %x PC\n", guard, *guard);
}

獲取函數(shù)符號(hào)

使用-fsanitize-coverage=func,trace-pc-guard 編譯器將會(huì)在每個(gè)函數(shù)的邊緣插入 __sanitizer_cov_trace_pc_guard 函數(shù)涎跨。 __builtin_return_address(0) 返回當(dāng)前函數(shù)返回地址也就是當(dāng)前函數(shù)的調(diào)用者。我們通過 dladdr()函數(shù)根據(jù) PC 指針可以獲取到其相關(guān)信息崭歧。

#import <dlfcn.h>
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
  void *PC = __builtin_return_address(0);
    Dl_info info;
    dladdr(PC, &info);
    printf("sname:%s \nsaddr:%p \n",info.dli_sname,info.dli_saddr);
}

dli_sname

可以查看到 info.dli_sname 是我們想要的函數(shù)符號(hào)信息隅很,我們可以將 App 啟動(dòng)過程中將這些信息去重存儲(chǔ)到數(shù)組中,啟動(dòng)完成后在沙盒中新建 .order 文件驾荣,并將數(shù)據(jù)寫入外构。

最后,將 .order 文件從沙盒中取出播掷,配置到工程 Order File 审编。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市歧匈,隨后出現(xiàn)的幾起案子垒酬,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勘究,死亡現(xiàn)場(chǎng)離奇詭異矮湘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)口糕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門缅阳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人景描,你說我怎么就攤上這事十办。” “怎么了超棺?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵向族,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我棠绘,道長(zhǎng)件相,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任氧苍,我火速辦了婚禮夜矗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘候引。我一直安慰自己侯养,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布澄干。 她就那樣靜靜地躺著逛揩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪麸俘。 梳的紋絲不亂的頭發(fā)上辩稽,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音从媚,去河邊找鬼逞泄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛拜效,可吹牛的內(nèi)容都是我干的喷众。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼紧憾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼到千!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起赴穗,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤憔四,失蹤者是張志新(化名)和其女友劉穎膀息,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體了赵,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡潜支,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了柿汛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冗酿。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖苛茂,靈堂內(nèi)的尸體忽然破棺而出已烤,到底是詐尸還是另有隱情,我是刑警寧澤妓羊,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站稍计,受9級(jí)特大地震影響躁绸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜臣嚣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一净刮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧硅则,春花似錦淹父、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至大审,卻和暖如春蘸际,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背徒扶。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工粮彤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人姜骡。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓导坟,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親圈澈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子惫周,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容