這兩天 Uber 的開發(fā)團隊在一個大會上分享了用 Swift 3 重寫客戶端的過程, 視頻里介紹了一個很黑科技的技巧, 可以極大地加快編譯速度, 我自己試了一下之后發(fā)現(xiàn)確實有效, 但也有小坑, 在這里跟大家分享一下.
Uber 的開發(fā)團隊偶然發(fā)現(xiàn)如果把所有 Model 文件全部合并到一個文件去編譯, 那編譯時間會從 1min 35s 減少到 17s, 那么我們?nèi)绻阉写a文件都合并到一起, 那就可以極大地優(yōu)化編譯速度了.
WHO(Whole-Module-Optimization) 也會把文件合并起來再進行編譯, 實際使用時我們發(fā)現(xiàn)編譯雖然快了, 但對于編譯時間的減少還是遠沒有直接把文件合并到一起那么有效. 主要原因是因為 WHO 除了合并文件之外, 還會在預編譯階段做這些事情:
- 檢測沒有被調(diào)用的方法和類型, 在預編譯期去掉它們
- 給沒有被繼承的類, 沒有被繼承的方法加上 final 標簽, 給編譯器提供更多信息, 以便這些方法被優(yōu)化為靜態(tài)調(diào)用或者是內(nèi)聯(lián)進去
這些優(yōu)化會對于程序的效率有很大的提升, 但編譯時需要加載更多的 context, 每合并一個文件, 就會遍歷所有文件進行一次上面的檢查, 編譯時間會隨著文件的增多呈指數(shù)級增長.
Uber 的團隊發(fā)現(xiàn)通過增加一個編譯宏就可以做到只合并文件, 而不做優(yōu)化. 進入工程文件設置 -> Build Setting -> Add User-Defined Settings, key 為 SWIFT_WHOLE_MODULE_OPTIMIZATION
, value 設為 YES
, 然后把優(yōu)化級別設為 None
就可以了.
那么問題來了, 為什么 Swift 的編譯器沒有進行這樣的優(yōu)化呢?
答案很簡單, 因為這種優(yōu)化會讓增量編譯的顆粒度從 File 級別增大到 Module 級別.
編譯的過程一般是每一個文件單獨進行編譯, 然后再鏈接到一起. 編譯后緩存的是鏈接前的產(chǎn)物, 而把所有文件都合并到一起再編譯, 緩存的就是合并后的文件編譯后的產(chǎn)物.
只要修改我們項目里的一個文件, 想要編譯 debug 一下, 就又得重新合并文件從頭開始編譯一次, 而不能讀取緩存跳過沒有被修改的文件.
但 pod 里的庫, storyboard 和 xib 文件就不會受這個影響, 只是我們修改了文件之后, 就得整個 module 從頭編譯一遍 (我們的項目也是一個 module, 只是 main 函數(shù)位于這個 module 而已, 除此之外跟別的 module 沒有任何本質(zhì)區(qū)別)
所以這個優(yōu)化手段其實沒想象中那么有用, 反正打包一般都是 CI 去做, 不在本機, 而日常 debug 都會直接增量編譯. 只有到了 Uber 這種規(guī)模的團隊, 每一個 feature branch 都需要到 CI 上打包測試, 使用這種優(yōu)化手段才比較有現(xiàn)實意義.
非要說對于普通開發(fā)者的意義的話, 可能是 flow.ci 那種按照時長計費的方式可以省點錢, 畢竟內(nèi)部測試的時候?qū)π阅芤鬀]那么高. (仔細想想, 好像還可以省蠻多錢的, 小型項目幾萬行代碼使用這種優(yōu)化的話, 之前編譯一次的費用現(xiàn)在可以用三四次, 而且收益也會隨著項目增大呈指數(shù)級增長)
喜歡我寫的文章的話可以關注我的博客