iOS 啟動優(yōu)化(一) pre-main

如何精確度量 iOS App 的啟動時間

iOS啟動分為兩個時間:

  1. pre-main時間
  2. main時間

一、pre-main時間檢測

Xcode 提供了一個很贊的方法倦卖,只需要在 Edit scheme -> Run -> Arguments 中將環(huán)境變量 DYLD_PRINT_STATISTICS 設(shè)為 1咨察,就可以看到 main 之前各個階段的時間消耗

Total pre-main time: 341.32 milliseconds (100.0%)
         dylib loading time: 154.88 milliseconds (45.3%)
        rebase/binding time:  37.20 milliseconds (10.8%)
            ObjC setup time:  52.62 milliseconds (15.4%)
           initializer time:  96.50 milliseconds (28.2%)
           slowest intializers :
               libSystem.dylib :   4.07 milliseconds (1.1%)
    libMainThreadChecker.dylib :  30.75 milliseconds (9.0%)
                  AFNetworking :  19.08 milliseconds (5.5%)
                        LDXLog :  10.06 milliseconds (2.9%)
                        Bigger :   7.05 milliseconds (2.0%)

還有一個方法獲取更詳細(xì)的時間馋袜,只需將環(huán)境變量 DYLD_PRINT_STATISTICS_DETAILS 設(shè)為 1 就可以柬祠。

  total time: 2.8 seconds (100.0%)
  total images loaded:  488 (471 from dyld shared cache)
  total segments mapped: 61, into 24958 pages
  total images loading time: 1.1 seconds (40.6%)
  total load time in ObjC:  92.39 milliseconds (3.2%)
  total debugger pause time: 794.39 milliseconds (28.2%)
  total dtrace DOF registration time:   0.00 milliseconds (0.0%)
  total rebase fixups:  921,005
  total rebase fixups time: 109.77 milliseconds (3.9%)
  total binding fixups: 694,265
  total binding fixups time: 766.41 milliseconds (27.2%)
  total weak binding fixups time:   9.05 milliseconds (0.3%)
  total redo shared cached bindings time: 768.13 milliseconds (27.3%)
  total bindings lazily fixed up: 0 of 0
  total time in initializers and ObjC +load: 690.73 milliseconds (24.5%)
                         libSystem.B.dylib :  11.67 milliseconds (0.4%)
               libBacktraceRecording.dylib :  12.06 milliseconds (0.4%)
                           libobjc.A.dylib :   6.09 milliseconds (0.2%)
                libMainThreadChecker.dylib :  59.50 milliseconds (2.1%)
              libViewDebuggerSupport.dylib :   7.66 milliseconds (0.2%)
                      libglInterpose.dylib : 286.97 milliseconds (10.2%)
                       libMTLCapture.dylib :   4.28 milliseconds (0.1%)
                          AWUnityFramework : 103.15 milliseconds (3.6%)
                           AiWayFashionCar : 365.65 milliseconds (12.9%)
total symbol trie searches:    1594338
total symbol table binary searches:    0
total images defining weak symbols:  63
total images using weak symbols:  133

1. 優(yōu)化(dylib loading time):

在項目優(yōu)化實踐中鸟赫,我們移除了一個沒有必要的動態(tài)庫淤毛,并將幾個動態(tài)庫合成為一個動態(tài)庫秋度,減少動態(tài)庫數(shù)量

第一個階段:pre-main time 中第一個階段 dylib loading time : 動態(tài)庫加載階段

***注: ***
如何查看動態(tài)庫的個數(shù): Products 中.app中會有Frameworks文件夾 里面即App需要引入的動態(tài)庫

舉例:Flutter優(yōu)化日志

這里區(qū)分兩種方式加載Flutter:

區(qū)別:
1. 第一種方式會將flutter依賴的第三方插件做成pod子倉的形式直接引入的源碼,
App.framework Flutter.framework
2. 第二種方式會將flutter依賴的第三方插件做成framework,之后將所有的framework做成pod倉庫
App.framework, FlutterPluginRegistrant.framework ,shared_preferences.framework, wakelock.framework , FMDB.framework , flutter_boost.framework , sqflite.framework , webview_flutter.framework, Flutter.framework , path_provider.framework , video_player.framework

造成的后果是: 第二種主工程會引入很多framework,造成的影響是動態(tài)庫加載時間變長

第一種:采取直接污染主工程方式: 由于日志較多,僅展示敏感數(shù)據(jù)

Total pre-main time: 685.23 milliseconds (100.0%)
        dylib loading time: 152.81 milliseconds (22.3%)
      rebase/binding time:  74.06 milliseconds (10.8%)
         ObjC setup time:  44.86 milliseconds (6.5%)
        initializer time: 413.48 milliseconds (60.3%)
Total pre-main time: 980.53 milliseconds (100.0%)
        dylib loading time: 163.32 milliseconds (16.6%)
       rebase/binding time:  86.59 milliseconds (8.8%)
           ObjC setup time: 223.50 milliseconds (22.7%)
          initializer time: 507.11 milliseconds (51.7%)

第二種:采取pod倉庫形式

Total pre-main time: 818.21 milliseconds (100.0%)
        dylib loading time: 243.54 milliseconds (29.7%)
       rebase/binding time:  72.09 milliseconds (8.8%)
           ObjC setup time:  39.28 milliseconds (4.8%)
          initializer time: 463.27 milliseconds (56.6%)
Total pre-main time: 1.3 seconds (100.0%)
        dylib loading time: 270.75 milliseconds (20.3%)
       rebase/binding time:  69.74 milliseconds (5.2%)
           ObjC setup time: 282.78 milliseconds (21.2%)
          initializer time: 708.54 milliseconds (53.2%)

對比結(jié)果: dylib loading time 時間拉長了100多毫秒

技術(shù)選擇:

  • 第一種方式污染主工程: 可以看到結(jié)果是framework變少
  • 第二種方式極少的污染主工程: 結(jié)果是framework變多

2. 優(yōu)化(rebase/binding time):

這一階段系統(tǒng)主要注冊 Objc 類。所以钱床,指針數(shù)量越少越好荚斯。這一步能做的優(yōu)化有:

  • 清理項目中無用的類
  • 刪減沒有被調(diào)用到或者已經(jīng)廢棄的方法
  • 刪減一些無用的靜態(tài)變量

核心思想是在進(jìn)行動態(tài)庫的重定位和綁定(Rebase/binding)(ASLR:dylib會被加載到隨機地址,這個隨機的地址跟代碼和數(shù)據(jù)指向的舊地址會有偏差查牌,dyld 需要修正這個偏差事期,做法就是將 dylib 內(nèi)部的指針地址都加上這個偏移量) 過程中減少指針修正;
減少Objective-C類數(shù)量纸颜,減少分類兽泣,減少實例變量和函數(shù)(刪除不用的類以及冗余代碼,再深一點就是減少第三方工具的使用胁孙,可以查看源碼唠倦,自己實現(xiàn));
減少C++虛函數(shù)涮较;

3.優(yōu)化(ObjC setup time):

Objc Setup Time
這一步主要做了以下操作
注冊O(shè)bjc類 (class registration)
把category的定義插入方法列表 (category registration)
保證每一個selector唯一 (selctor uniquing)
前兩部做好之后這一步就沒有什么可以有優(yōu)化的

4. 優(yōu)化(initializer time):

第一個階段:pre-main time 中第4個階段 initializer time :

這一階段 dyld開始運行程序的初始化函數(shù),調(diào)用每個Obj類和分類的+load方法,
這一階段稠鼻,dyld 開始運行程序的初始化函數(shù),調(diào)用每個 Objc 類和分類的 +load 方法狂票,調(diào)用 C/C++ 中的構(gòu)造器函數(shù)候齿。initializer階段執(zhí)行完后,dyld 開始調(diào)用 main() 函數(shù)。在這一步慌盯,檢查 +load 方法周霉,盡量把事情推遲到 +initiailize 方法里執(zhí)行。

  • 使用initialize替代load方法
  • 減少使用c/c++的attribute((constructor))亚皂;推薦使用dispatch_once(),peathrd_once(), std:once()等方法
  • 不要在初始化中創(chuàng)建線程
  • 推薦使用swift
優(yōu)化前
Total pre-main time: 731.83 milliseconds (100.0%)
         dylib loading time: 250.62 milliseconds (34.2%)
        rebase/binding time:  50.32 milliseconds (6.8%)
            ObjC setup time:  37.81 milliseconds (5.1%)
           initializer time: 393.07 milliseconds (53.7%)
           slowest intializers :
             libSystem.B.dylib :   6.20 milliseconds (0.8%)
    libMainThreadChecker.dylib :  28.41 milliseconds (3.8%)
          libglInterpose.dylib : 187.20 milliseconds (25.5%)
               AiWayFashionCar : 244.98 milliseconds (33.4%)
優(yōu)化后
Total pre-main time: 667.68 milliseconds (100.0%)
         dylib loading time: 237.69 milliseconds (35.6%)
        rebase/binding time:  53.64 milliseconds (8.0%)
            ObjC setup time:  36.47 milliseconds (5.4%)
           initializer time: 339.86 milliseconds (50.9%)
           slowest intializers :
             libSystem.B.dylib :   6.05 milliseconds (0.9%)
    libMainThreadChecker.dylib :  28.70 milliseconds (4.2%)
          libglInterpose.dylib : 152.15 milliseconds (22.7%)
               AiWayFashionCar : 218.33 milliseconds (32.7%)

// 經(jīng)全局查找看到有一個load方法里面執(zhí)行了IO操作相關(guān)API, 優(yōu)化之后 initializer time 有所優(yōu)化

二俱箱、main 之后的時間度量

main 到 didFinishLaunching 結(jié)束或者第一個 ViewController 的viewDidAppear 都是作為 main 之后啟動時間的一個度量指標(biāo)。

這個時間統(tǒng)計直接打點計算就可以灭必,不過當(dāng)遇到時間較長需要排查問題時匠楚,只統(tǒng)計兩個點的時間其實不方便排查,目前見到比較好用的方式就是為把啟動任務(wù)規(guī)范化厂财、粒子化芋簿,針對每個任務(wù)都有打點統(tǒng)計,這樣方便后期問題的定位和優(yōu)化璃饱。

以此記錄優(yōu)化啟動日志

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末与斤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子荚恶,更是在濱河造成了極大的恐慌撩穿,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谒撼,死亡現(xiàn)場離奇詭異食寡,居然都是意外死亡,警方通過查閱死者的電腦和手機廓潜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進(jìn)店門抵皱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人辩蛋,你說我怎么就攤上這事呻畸。” “怎么了悼院?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵伤为,是天一觀的道長。 經(jīng)常有香客問我据途,道長绞愚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任颖医,我火速辦了婚禮位衩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘便脊。我一直安慰自己蚂四,他們只是感情好光戈,可當(dāng)我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布哪痰。 她就那樣靜靜地躺著遂赠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晌杰。 梳的紋絲不亂的頭發(fā)上跷睦,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天,我揣著相機與錄音肋演,去河邊找鬼抑诸。 笑死,一個胖子當(dāng)著我的面吹牛爹殊,可吹牛的內(nèi)容都是我干的蜕乡。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼梗夸,長吁一口氣:“原來是場噩夢啊……” “哼层玲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起反症,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤辛块,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后铅碍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體润绵,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年胞谈,在試婚紗的時候發(fā)現(xiàn)自己被綠了尘盼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡烦绳,死狀恐怖悔叽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情爵嗅,我是刑警寧澤娇澎,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站睹晒,受9級特大地震影響趟庄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伪很,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一戚啥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锉试,春花似錦猫十、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贷笛。三九已至,卻和暖如春宙项,著一層夾襖步出監(jiān)牢的瞬間乏苦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工尤筐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留汇荐,地道東北人。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓盆繁,卻偏偏與公主長得像掀淘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子油昂,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,860評論 2 361

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