iOS Dynamic Framework 對 App 啟動時間影響實測

發表於2016-11-26

最近看到的Slow App Startup Times裡提到:

The dynamic loader finds and reads the dependent dynamic libraries (dylibs) used by the App. Each library can itself have dependencies. The loading of Apple system frameworks is highly optimized but loading your embedded frameworks can be expensive. To speed up dylib loading Apple suggests you use fewer dylibs or consider merging them A suggested target is for six extra (non-system) frameworks.

至於什麼是static lib什麼是dynamic lib這裡就不展開提了,大家可以通過在文章底部的相關連結去詳細瞭解。

引用一下官方的解釋就是:

  • A better approach is for an app to load code into its address space when it’s actually needed, either at launch time or at runtime. The type of library that provides this flexibility is called dynamic library.
  • When an app is launched, the app’s code—which includes the code of the static libraries it was linked with—is loaded into the app’s address space.Applications with large executables suffer from slow launch times and large memory footprints

如果給你一個簡單的概念就是如果你的framework使用了swift就是dynamic lib。這也是本文要對比的主題:動態的framework對app啟動時間影響到底多大。

測試的方法就採用 iOS 10 提供的新的環境變數 DYLD_PRINT_STATISTICS 輸出的app啟動時間。Xcode的版本是8.1,測試裝置是iphone 6。cocoapod版本1.1。

注意,測試過程發現每次獲得的時間統計都不一致,所以我這裡的資料可能和你自己測試得到的不同,但是我認為這種偏差不影響定性。

基準線:空的OC專案 VS 空的Swift專案

建立兩個沒有任何業務邏輯的空的專案。

11225849-09f79dbf954d50af
純OC專案的啟動時間
12225849-5057bb57ca81de78
純swift空專案的啟動時間

大概有10毫秒的差異。這個差距考慮到測量的偏差可以認為兩者幾乎是一致的。

但是有時會出現swift載入忽然提高到400ms的情況。這是因為系統的動態framework只會載入一份。假設10個app啟動都用到了UIKit,系統內部也只載入了一份UIKit。所以有時swift專案啟動的時候剛好用到了系統framework沒有快取,就會顯得的長一點。

6個framework

現在我們對比有程式碼的情況。兩個專案分別加入5個依賴。
這是OC專案的6個依賴:

這是swift專案的6個依賴,為了模擬真實生產中依然使用一些OC庫的情況,將3個庫換成了swift編碼的,保留了3個OC庫:

OC的啟動時間在70-100ms左右。這裡取快的情況的資料:

13225849-e2eec1df0a68b950
OC6個依賴的啟動時間

swift專案在第一次安裝時的啟動時間在dylib會多100ms,不知為何。swift專案在已經安裝後執行開啟的成績:

14225849-633c98793e75da23
swift6個依賴

列一個橫向對比圖:

15225849-684c150d13249ad0

結論

swift專案的dylib時間相比OC多了一百多毫秒,遠遠大於OC。

Swift 不使用framework VS 使用framework

我們猜測是不是載入framework導致時間增大很多呢。所以我們把這些依賴的程式碼全部放在主app裡,不使用framework分離。這次我們把幾個依賴的庫全部換成swift:

5個依賴的專案在app已經安裝,執行開啟結果:

16225849-b96659ef82d682e9
5個依賴

考慮到誤差可以認為framework裡的程式碼是OC還是swift對於載入時間的影響並不大。
把依賴的程式碼全都合併到App裡,就是採取手工拷貝的方式:

17225849-1e382363f1055dc2

安裝後開啟的啟動時間統計:

18225849-2e6ff8566ab21d35
對比圖:
19225849-1d4234bee854eaa6

結論

如果把程式碼收到拷貝進app裡,可以顯著降低dylib載入時間。

15個framework OC VS Swift

OC的podfile:

swift專案的podfile:

OC的結果:

20225849-28a122ce5c15290e

swift的結果:

21225849-f5d2de5ed0e05c92

對比圖:

22225849-be809d25e260b40c
時間對比

縱向對比:

23225849-c5e6b82faec240d0

結論

OC的專案隨著依賴增多,初始化時間增大,但是dylib時間增加不明顯。swift則dylib時間大幅增加,初始化時間變化不大。總的啟動時間比OC多了200多毫秒,隨著framework的增多,啟動時間差距拉大。

25個dynamic framework

如果把dylib提高到25個時間又會增長多少呢。
在15個基礎上再新增10個依賴:

執行時間:

24225849-a2307baa1e5e167e

和15個dylib的時候的對比:

25225849-a1059dd50bbdc062

dylib載入時間從400增加到600,增加了30%左右。dylib數量從15到25個增加40%。接近線性。

總結

毫無疑問,使用了dynamic framework後會增加app啟動時間。如果你的數量在25個左右,相比OC的靜態framework啟動時間會增加0.5s左右。我個人對於iOS提高載入framework的時間不太抱有希望,蘋果讓自定義的framework常駐記憶體似乎也無望,這個時間短期內可能無法抹平。

如果人為的把一些外部依賴手動管理在一個framework也是可行,但是如果複雜一點包的互相依賴的情況會比較費心。

如果公司對效能有著苛刻要求可能500ms是難以忍受的。但是我覺得對於大多數產品而言,犧牲這500ms的效能相比於使用OC,我覺得還是用OC比較難受。

歡迎關注我的微博:@沒故事的卓同學


相關連結:
WWDC 2016 Session 406 Optimizing App Startup Time
What are Frameworks?
iOS 開發中的『庫』(一)
jverkoey/iOS-Framework

相關文章