怎樣提高iOS工程打包的速度
過慢的編譯速度有非常明顯的副作用。一方面,程序員在等待打包的過程中可能會分心,比如刷刷朋友圈,看條新聞等等。這種認知上下文的切換會帶來很多隱形的時間浪費。另一方面,大部分 app 都有自己的持續集成工具,如果打包速度太慢, 會影響整個團隊的開發進度。
因此,本文會分別討論日常開發和持續集成這兩種場景,分析打包速度慢的瓶頸所在,以及對應的解決方案。利用這些方案,筆者成功的把公司 app 的持續集成時間從 45 min 成功的減少到 9 min,效率提升高達 80%,理論上打包速度可以提升 10 倍以上。如果用一句話總結就是:
在絕對的實力(硬件)面前,一切技巧(軟件)都是浮云
日常開發
其實日常開發的優化空間并不大,因為默認情況下 Xcode 會使用上次編譯時留下的緩存,也就是所謂的增量編譯。因此,日常開發的主要耗時由三部分構成:
總耗時 = 增量編譯 + 鏈接 + 生成調試信息(dSYM)
這里的增量編譯耗時比較短,即使是在我 14 年高配的 MacBook Pro(4核心,8 線程,2.5GHz i7 4870HQ,下文簡稱 MBP) 上,也僅僅耗時十秒上下。我們的應用代碼量大約一百多萬行,業內超過這個量級的應用應該不多。鏈接和生成調試信息各花費不到 20s,因此一次增量的編譯的時間開銷在半分鐘到一分鐘左右,我們逐個分析:
增量編譯: 因為耗時較短(大概十幾秒或者更少),幾乎不存在優化的空間,但是非常容易惡化。因為只有頭文件不變的編譯單元才能被緩存,如果某個文件被 N 個文件引用,且這個文件的頭文件發生了變化,那么這 N 個文件都會重編譯。APP 的分層架構一般都會做,但一個典型的誤區是在基礎庫的頭文件中使用宏定義,比如定義一些全局都可以讀取的常量,比如是否開啟調試,服務器的地址等等。這些常量一旦改變(比如為了調試或者切換到某些分支)就會導致應用重編譯。
鏈接:鏈接沒有緩存,而且只能用單核進行,因此它的耗時主要取決于單核性能和磁盤讀寫速度。考慮到我們的目標文件一般都比較小,因此 4K 隨機讀寫的性能應該會更重要一些。
調試信息:日常開發時,并不需要生成 dSYM 文件,這個文件主要用于崩潰時查找調用棧,方便線上應用進行調試,而開發過程中的崩潰可以直接在 Xcode 中看到,關閉這個功能 不會對開發產生任何負面影響。
日常開發的優化空間不大,即使是龐大的項目,落后的機器性能,關閉 dSYM 以后也就耗時 30s 左右。相比之下,打包速度可以優化和討論的地方就比較多了。
持續集成
在利用 Jenkins 等工具進行持續集成時,緩存不推薦被使用。這是因為蘋果的緩存不夠穩定,在某些情況下還存在 bug。比如明明本地已經修復了 bug,可以編譯通過,但上次的編譯緩存沒有被正確清理,導致在打包機器上依然無法編譯通過。或者本地明明寫出了 bug,但同樣由于緩存問題,打包機器依然可以編譯通過。
因此,無論是手動刪除 Derived Data 文件夾,還是調用 xcodebuild clean 命令,都會把緩存清空。或者直接使用 xcodebuild archive,會自動忽略緩存。每次都要全部重編譯是導致打包速度慢的根本原因。以我們的項目為例,總計 45min 的打包時間中,有 40min 都在執行 xcodebuild 這一行命令。
使用 CCache 緩存
最自然的想法就是使用緩存了,既然蘋果的緩存不靠譜,那么就找一個靠譜的緩存,比如 CCache。它是基于編譯器層面的緩存,根據目前反饋的情況看,并不存在緩存不一致的問題。根據筆者的實驗,使用 CCache 確實能夠較大幅度的提升打包速度,刪除緩存并使用 CCache 重編譯后,耗時只有十幾分鐘。
然而,CCache 最致命的問題是不支持 PCH 文件和 Clang modules。PCH 的本意是優化編譯時間,我們假設有一個頭文件 A 依賴了 M 個頭文件,其中每個被依賴的頭文件又依賴了 N 個 頭文件,如下圖所示:
由于 #import 的本質就是把被依賴頭文件的內容拷貝到自己的頭文件中來,因此頭文件 A 中實際上包含了 M * N 個頭文件的內容,也就需要 M * N 次文件 IO 和相關處理。當項目中每增加一個依賴頭文件 A 的文件,就會重復一次上述的 M * N 復雜度的過程。
PCH 文件的好處是,這個文件中的頭文件只會被編譯一次并緩存下來,然后添加到項目中 所有 的頭文件中去。上述問題倒是解決了,但很智障的一點是,所有文件都會隱式的依賴所有 PCH 中的文件,而真正需要被全局依賴的文件其實非常少。因此實際開發中,更多的人會把 PCH 當成一種快速 import 的手段,而非編譯性能的優化。前文解釋過,PCH 文件一旦發生修改,會導致徹徹底底,完完整整的項目重編譯,從而降低編譯速度。正是因為 PCH 的副作用甚至抵消了它帶來的優化,蘋果已經默認不使用 PCH 文件了。
用來取代 PCH 的就是 Clang modules 技術,對于開啟了這一選項的項目,我們可以用@import 來替代過去的 #import,比如:
@import UIKit;
等價于
#import 《UIKit/UIKit.h》
拋開自動鏈接 framework 這些小特性不談,Clang modules 可以理解為模塊化的 PCH,它具備了 PCH 可以緩存頭文件的優點,同時提供了更細粒度的引用。
說回到 CCache,由于它不支持 PCH 和 Clang modules,導致無法在我們的項目中應用。即使可以用,也會拖累項目的技術升級,以這種代價來換取緩存,只怕是得不償失。
distcc
distcc 是一種分布式編譯工具,可以把需要被編譯的文件發送到其他機器上編譯,然后接收編譯產物。然而,經過貼吧、貝聊、手Q 等應用的多方實驗,發現并不適合 iOS 應用。它的原理是多個客戶端共同編譯,但是絕大多數文件其實編譯時間非常短,并不值得通過網絡來回傳送,這種方案應該只適合單個文件體量非常大的項目。在我們的項目中,使用 distcc大幅度增加了打包時間,大約耗時 1 小時左右。
定位瓶頸
在尋求外部工具無果后,筆者開始嘗試著對編譯時間直接做優化。為了搞清楚這 40min 究竟是如何花費的,我首先對 xcodebuild 的輸出結果進行詳細分析。
使用過 xcodebuild 命令的人都會知道,它的輸出結果對開發者并不友好,幾乎沒有可讀性,好在還有 xcpretty 這個工具可以格式化它:
gem install xcpretty
通過 gem 安裝后,只要把 xcodebuild 的輸出結果通過管道傳給 xcpretty 即可:
xcodebuild -scheme Release 。。. | xcpretty
非常好我支持^.^
(0) 0%
不好我反對
(0) 0%
下載地址
怎樣提高iOS工程打包的速度下載
相關電子資料下載
- iOS17.1可能明天發布,iOS17.1主要修復哪些問題? 376
- 華為全新鴻蒙蓄勢待發 僅支持鴻蒙內核和鴻蒙系統應用 719
- 蘋果手機系統iOS 17遭用戶質疑 731
- iPhone12輻射超標?蘋果推送iOS 17.1解決此事 750
- 傳華為囤積零部件 目標明年智能手機出貨7000萬部;消息稱 MiOS 僅限國內,小米 28208
- 蘋果推送iOS17.0.3,解決iPhone15Pro系列存在機身過熱 216
- Testin云測兼容和真機服務平臺中上線iPhone 15系列手機 208
- 利爾達推出搭載HooRiiOS的Matter模組 145
- 運放參數解析:輸入偏置電流(Ibias)和失調電流(Ios) 128
- 昆侖太科發布支持國產飛騰騰銳D2000芯片的開源BIOS固件版本 448