[TOC]程序員
過慢的編譯速度有很是明顯的反作用。一方面,程序員在等待打包的過程當中可能會分心,好比刷刷朋友圈,看條新聞等等。這種認知上下文的切換會帶來不少隱形的時間浪費。另外一方面,大部分 app 都有本身的持續集成工具,若是打包速度太慢, 會影響整個團隊的開發進度。shell
所以,本文會分別討論平常開發和持續集成這兩種場景,分析打包速度慢的瓶頸所在,以及對應的解決方案。利用這些方案,筆者成功的把公司 app 的持續集成時間從 45 min 成功的減小到 9 min,效率提高高達 80%,理論上打包速度能夠提高 10 倍以上。若是用一句話總結就是:npm
在絕對的實力(硬件)面前,一切技巧(軟件)都是浮雲xcode
其實平常開發的優化空間並不大,由於默認狀況下 Xcode 會使用上次編譯時留下的緩存,也就是所謂的增量編譯。所以,平常開發的主要耗時由三部分構成:緩存
總耗時 = 增量編譯 + 連接 + 生成調試信息(dSYM)性能優化
這裏的增量編譯耗時比較短,即便是在我 14 年高配的 MacBook Pro(4核心,8 線程,2.5GHz i7 4870HQ,下文簡稱 MBP) 上,也僅僅耗時十秒上下。咱們的應用代碼量大約一百多萬行,業內超過這個量級的應用應該很少。連接和生成調試信息各花費不到 20s,所以一次增量的編譯的時間開銷在半分鐘到一分鐘左右,咱們逐個分析:bash
平常開發的優化空間不大,即便是龐大的項目,落後的機器性能,關閉 dSYM 之後也就耗時 30s 左右。相比之下,打包速度能夠優化和討論的地方就比較多了。服務器
在利用 Jenkins 等工具進行持續集成時,緩存不推薦被使用。這是由於蘋果的緩存不夠穩定,在某些狀況下還存在 bug。好比明明本地已經修復了 bug,能夠編譯經過,但上次的編譯緩存沒有被正確清理,致使在打包機器上依然沒法編譯經過。或者本地明明寫出了 bug,但一樣因爲緩存問題,打包機器依然能夠編譯經過。網絡
所以,不管是手動刪除 Derived Data
文件夾,仍是調用 xcodebuild clean
命令,都會把緩存清空。或者直接使用 xcodebuild archive
,會自動忽略緩存。每次都要所有重編譯是致使打包速度慢的根本緣由。以咱們的項目爲例,總計 45min 的打包時間中,有 40min 都在執行 xcodebuild
這一行命令。架構
最天然的想法就是使用緩存了,既然蘋果的緩存不靠譜,那麼就找一個靠譜的緩存,好比 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 是一種分佈式編譯工具,能夠把須要被編譯的文件發送到其餘機器上編譯,而後接收編譯產物。然而,通過貼吧、貝聊、手Q 等應用的多方實驗,發現並不適合 iOS 應用。它的原理是多個客戶端共同編譯,可是絕大多數文件其實編譯時間很是短,並不值得經過網絡來回傳送,這種方案應該只適合單個文件體量很是大的項目。在咱們的項目中,使用 distcc
大幅度 增長了打包時間,大約耗時 1 小時左右。
在尋求外部工具無果後,筆者開始嘗試着對編譯時間直接作優化。爲了搞清楚這 40min 到底是如何花費的,我首先對 xcodebuild
的輸出結果進行詳細分析。
使用過 xcodebuild
命令的人都會知道,它的輸出結果對開發者並不友好,幾乎沒有可讀性,好在還有 xcpretty
這個工具能夠格式化它:
gem install xcpretty複製代碼
經過 gem
安裝後,只要把 xcodebuild
的輸出結果經過管道傳給 xcpretty
便可:
xcodebuild -scheme Release ... | xcpretty複製代碼
下面是官方文檔中的 Demo:
我只對其中的編譯部分感興趣,因此簡單的作下過濾,咱們就能夠獲得格式高度統一的輸出:
Compiling A.m
Compiling B.m
Compiling ...
Compiling N.m複製代碼
到了這一步,終於能夠作最關鍵的計算了,咱們能夠經過設置定時器,計算相鄰兩行輸出之間的間隔,這個間隔就是文件的編譯時間。固然,也有相似的輔助工具作好了這個邏輯:
npm install gnomon複製代碼
簡單的作一下排序,就能夠看到最耗時的前 200 個文件了,還能夠針對文件後綴做區分,計算總耗時等等。通過排查,咱們發現一半的編譯時間都花在了編譯 protobuf 文件上。
除了針對超長耗時的文件進行 case-by-case 的分析外,另外一種方案是調整工程設置。通常來講,咱們的持續集成工具主要是用來給產品經理或者測試人員使用,用來體驗功能或者驗證 Bug,除非是須要上架 App Store,不然並不須要關心運行時性能。然而在手機上使用的 Release 模式,默認會開啓各類優化,這些優化都是犧牲編譯性能,換取運行時速度,對於上架的包而言無可厚非,但對於那些 Daily Build 包來講,就顯得得不償失了。
所以,加速打包的思路和優化的思路是徹底互逆的,咱們要作的就是關閉一切可能的優化。這裏推薦一篇文章:關於Xcode編譯性能優化的研究工做總結,能夠說至關全面了。
通過對其中各個參數的查找資料和嘗試關閉,按照提高速度的降序排列,簡單整理幾個:
Optimize level
改爲 O0,表示不作任何優化。Derived Data
目錄下,所以若是內存足夠,能夠考慮劃出 4G 左右的內存,建一個虛擬磁盤,這樣將會把磁盤 IO 優化爲 內存 IO,從而提升速度。因爲打包機器每次都會重編譯,所以並不須要擔憂重啓機器後緩存丟失的問題。在以上幾個操做中,精簡指令集的做用最大,大約能夠把編譯時間從 45 min 減小到 30min 之內,配合關閉編譯優化,能夠進一步把打包時間減小到 20min。虛擬磁盤大約能夠減小兩三分鐘的編譯時間,dSYM 耗時大約二十秒,其它選項的優化程度更低,大約在幾秒左右,沒有精確測算。
所以,通常來講 只要精簡指令集並關閉優化便可,有條件的機器可使用虛擬磁盤,不建議再作其它修改。
二進制化主要指的是利靜態庫代替源碼,避免編譯。前文已經介紹過如何分析文件的耗時,所以二進制化的收益很是容易計算出來。因爲團隊分工問題,筆者沒有什麼二進制化的經驗,通常來講這個優化比較適合基礎架構組去實施。
以上主要是經過修改軟件的方式來加速打包,自從公司申請了 2013 年款 Mac Pro(Xeon-E5 1630 6 核 12 線程,16G 內存,256G SSD 標配,下文簡稱 Mac Pro)後,不須要修改任何配置,僅僅是簡單的遷移打包機器,就能夠把打包時間下降到 15 min,配和上一節中的前三條優化,最終的打包時間大概在 10min 之內。
在個人黑蘋果(i7 7820x 8 核 16 線程,16G 內存,三星 PM 961 512G SSD,下文簡稱黑蘋果)上,即便不開啓任何優化,從零開始編譯也僅需 5min。若是將 protobuf 文件二進制化,再配合一些工程設置的優化,我不敢想象須要花多長時間,預計在 4min 左右吧,速度提高了大概 11 倍。
編譯是一個考驗多核性能的操做,在個人黑蘋果上,編譯時能夠看到 8 個 CPU 的負載都達到了 100%,所以在必定範圍內(好比 10 核之內),提高 CPU 核數遠比提高單核主頻對編譯速度的影響大。至於某些 20 核以上、單核性能較低的 CPU 編譯性能如何,但願有經驗的讀者給予反饋。
下表總結了文章中提到的各類優化手段帶來的速度提高,參考原始時間均爲 45 min(打包機器:13 寸 MacBook Pro):
方案序號 | 優化方案 | 優化後耗時 (min) | 時間減小百分比 |
---|---|---|---|
1 | 不常修改的文件二進制化 | 25 | 44.4% |
2 | 精簡指令集 | 27 | 40% |
3 | 關閉編譯優化 | 38 | 15.6% |
4 | 使用 Mac Pro | 15 | 66.7% |
5 | 虛擬磁盤 | 42 | 6.7% |
6 | 公司現行方案(2+3+4+5) | 9 | 80% |
7 | 黑蘋果 | 5 | 88.9% |
8 | 終極方案(1+2+3+5+7) | 4(預計) | 91.1%(預計) |
嚴格意義上講,文章有點標題黨了,由於一句話來講就是:
能用硬件解決的問題,就不要用軟件解決。