做者介紹:鍾子豪,貝聊科技高級 iOS 工程師git
以前一篇介紹 CCache 的文章探討了如何使用 CCache 來優化應用構建的時間,評論裏面收到了很多朋友反饋在使用的過程遇到了困難,最後沒法成功應用上 CCache。其中的絕大部分問題咱們在貝聊項目的集成過程當中也遇到過,本文主要針對這些問題給出相應的解決方案,並從其餘方面給出一些優化應用構建時間的建議。swift
經過命令 ccache -s
能夠查看 CCache 的總體緩存命中率。要使 CCache 真正能減小編譯時間,命中率大約要達到 90% 。前文已經提過,頻繁地緩存 miss 會比不使用 CCache 還慢,所以在集成 CCache 後須要保證90%的緩存命中率,才能確實地提升構建速度,若是發現緩存命中率太低,則須要分析日誌排查緣由。緩存
使用了 PCH 文件是最多見的致使緩存命中率太低的緣由。不少項目都會爲了方便,把一些經常使用的類在 PCH 中 import 一次,而在編譯時 PCH 的內容會附加在每一個文件以前,PCH 內容的改變會致使整個編譯對象的所有文件的內容改變,也就致使所有 CCache 緩存失效。若是你遇到過明明只修改了一兩個源文件,運行項目卻會全量編譯的狀況,看看是否是PCH裏面import了太多依賴,若是暫時沒法移除 PCH 則儘可能減小裏面 import 的文件。ruby
由於常見的 git 切換分支,pod update
等操做都會形成大量文件的最後編輯時間變化,若是用文件最後編輯時間做爲緩存的 key,常常會看見切換 git 分支或者 pod update
以後大量文件緩存失效。 CCache 默認就是用文件內容的 MD4 摘要值來做爲緩存 key 的一部分的,只要不加上 file_stat_matches
選項便可。雖然計算並對比文件內容的摘要值比簡單對比文件的修改時間和大小要耗費更長的時間,但通過實踐發現使用文件內容來做爲 key 會有更穩定的編譯優化效果,如下是我目前在用的效果比較好的 CCache 配置:bash
#!/bin/sh
if type -p ccache >/dev/null 2>&1; then
export CCACHE_MAXSIZE=10G
export CCACHE_CPP2=true
export CCACHE_HARDLINK=true
export CCACHE_SLOPPINESS=file_macro,time_macros,include_file_mtime,include_file_ctime,pch_defines
exec ccache /usr/bin/clang "$@"
else
exec clang "$@"
fi
複製代碼
在使用 Debug 配置開發期間,Xcode 自己自帶增量編譯,只是在使用 Release 配置構建 AdHoc 或者 AppStore 的時候不能使用增量編譯。所以在 Debug 模式下啓用 CCache 其實意義不大,開發時文件頻繁變動致使緩存命中率低,反而會拖慢平常開發節奏。併發
使用 CCache 須要關閉Clang Module
功能,而有些第三方庫由於使用了@import
語法致使若是關閉 Clang Module
則沒法使用@import
語法繼而致使編譯出錯。這種狀況能夠單獨設置一個 Pod 不要啓用 CCache 便可。app
下面的 Podfile 配置代碼同時演示瞭如何只在 Release 構建啓用 CCache 以及對單個 Pod 不使用 CCache:post
# Podfile
target 'YourApp' do # 替換爲你的 target 名
post_install do |installer_representation|
installer_representation.pods_project.targets.each do |target|
if config.name != 'Debug' && target.name != 'SomePod' # 替換爲你想要排除的 Pod 的名字
config.build_settings['CC'] = '$(PODS_ROOT)/ccache-clang' # 替換爲你的 ccache-clang 文件路徑
end
end
end
end
複製代碼
這些優化點與 CCache 無關,但我嘗試過以後發現對編譯速度和開發體驗有必定提高,所以一併列出。性能
更新:Xcode 9.3 發佈後,我又用一樣的項目驗證了一次,得出了近似的結果,但差別較小,建議先進行 Benchmark 再決定是否要應用到本身項目中。測試
我以前經過這條命令
defaults write com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks `sysctl -n hw.ncpu`
複製代碼
把編譯時的最大併發數設置爲 CPU 的核心數,沒想到這居然不是最好的配置。我最先是從下面這篇文章發現的: The best hardware to build with Swift is not what you might think | 領英
雖然原文做者說在 Xcode 9 已經修復了這個問題,但我仍是用一個純 Objective-C 的項目作實驗驗證,結果以下:
編譯併發數 | 編譯三次平均耗時(秒) |
---|---|
4 | 118.3 |
6 | 110.6 |
8 | 126.5 |
實驗結果也是讓我很意外,沒想到 8 線程的編譯速度竟然還不如 4 線程,雖然差距不算大,但經過調整編譯併發數對編譯速度的確有影響,你們能夠在本身的構建機器上用本身的項目實驗一下,找到最優的配置。
另外我順便用同一個項目測試了一下 Xcode 9 的 New Build System 的性能,結果以下
編譯併發數 | 編譯三次平均耗時(秒) |
---|---|
4 | 132.3 |
6 | 133.7 |
8 | 134.5 |
發現 New Build System 的編譯速度略遜於舊的編譯系統,但編譯過程更加嚴謹,例如在 Copy Bundle Resources 步驟時,若是發現缺乏了某個圖片資源文件會做爲錯誤而不是警告拋出。若是你的項目已經切換到了 New Build System,我建議繼續使用,畢竟將來蘋果會針對其作大量優化,這一點性能問題應該會獲得解決。
在排查編譯時間過長的元兇時,發如今項目的 Build Settings
-> Header Search Paths
中竟然有一行 ${PROJECT_DIR}/**
,多是之前某個同事遇到找不到頭文件編譯錯誤,沒有細想就加進去的。這項配置會致使編譯文件時在整個項目目錄中遞歸查找頭文件,顯著地增長查找頭文件的時間,單個文件的編譯時間可能會增長2~3倍之多。
沒有代碼的編譯速度能快得過「沒有代碼」,這是很顯而易見的優化手段,不過實現起來可能工做量較大。在公司業務快速迭代和更新時很容易就會產生一些再也不使用的模塊,按期清理既能減小項目的構建時間也能減少安裝包的體積,一箭雙鵰。若是以爲這些代碼之後還會用得上,能夠只移除 Xcode 項目的引用而保留下來文件以做參考。
clang 在編譯的過程當中若是遇到不規範的代碼,例若有未使用的變量,會生成一段警告信息並輸出到控制檯或者日誌,處理這些警告信息也是須要佔用一些的計算資源的。警告信息建議在開發階段就處理掉,若是警告信息是來自 CocoaPods 集成的第三方庫的話,能夠在 Podfile 中把inhibit_all_warnings!
選項打開。貝聊的項目經過這個操做把編譯時間減小了約 40 秒。
咱們的項目經過上面這些手段,最終把平均的編譯時間從 1300 多秒減小到 300 秒內,視乎 CCache 的命中率會有所浮動,但整體上是大幅減小,但願這些經驗能對你的項目有所幫助。