1. 程式人生 > >iOS開發中可以節省50%編譯等待時間的解決方案

iOS開發中可以節省50%編譯等待時間的解決方案

作為開發者,我們是不是經常在等待Xcode完成build的過程中,感覺iOS程式需要編譯很久,有時候,老闆迫切希望給他的手機裝上最新的版本,然後馬上出門見客戶,這個時候,你肯定是特別希望,Xcode編譯的時候,能夠快一點,我記得有一次,我就因為這個,而被老闆記住了。所以,當我們按下執行來測試修改過的內容,看著那幾乎無止境的等待迴圈。對於這種狀況我感覺特別浪費時間。

所以:如何縮短build時間,似乎是一個有待解決也很有趣的難題,也是一個瞭解OSX內部結構的大好機會。因此,我決定嘗試一下。非常幸運,取得了顯著效果:我減少了50%的等待時間。

解決方案

我的開發流程(也是最常見的一種情況)如下:

  1. 修改一些原始檔。
  2. 按下Xcode中的執行程式按鈕。
  3. 觀察在phone或者模擬器上的效果(我的例子中使用的是模擬器)。
  4. 跳到第一步。在我修改了一個Spotify的iOS客戶端中相對較小的 Objective C 原始檔之後,我記錄了一下步驟(2)到步驟(3)花費的時間,直到模擬器載入完應用程式:我的家用iMac(說實話,已經很舊了)花費了82秒(平均值)。通過觀察Xcode的編譯流程我意識到大部分時間花費在“Linking”和“Generating dSYM file”階段。

xcodegeneratingdsymfile

在命令列中進行一些測量證實了這一點,平均而言:

  • Linking花費了29秒
  • 生成dSYM 花費了25秒這兩個階段佔用了等待時間的(29 + 25) / 82 * 100 = 62 % 。但是,畢竟,Spotify的iOS客戶端程式碼庫是非常大的(連結器要把大約2000個目標檔案組合起來),花費這麼多時間或許也有些道理。然而,並非完全如此……

dSYM 檔案生成

老實說我對dSYM bundles瞭解不多,只知道其中包含除錯資訊。得知dSYM最初是作為Apple的一個“臨時解決方案”,我感到非常驚訝。事情是這樣的:在OSX早期階段,Apple為了避免在連結器中引入DWARF支援的麻煩,建立了獨立的連結器(dsymutil),這個連結器將除錯資訊從目標檔案中取出,放到一個同一個地方:一個dSYM bundle中。

dSYM bundles 對於發行版本有用,但在開發過程中並不需要。偵錯程式可以從中間目標檔案中獲取除錯資訊,這些檔案在 build 完成後仍然存在。

Xcode 允許開發者設定自己工程的 “Debug Information Format”,可以選擇使用“DWARF”而不是“DWARF 和 dSYM File”。

由此,在XCode中簡單地改變一個選項,就可以減少大約25秒等待時間:非常棒!

Linking

不幸的是,沒有神奇的選項可以跳過連結。或許,你也能跳過它,但是你最終會得到一大堆無用的目標檔案:)

最初,我認為縮短30秒連結時間(像永恆那麼久!)的唯一途徑是研究Apple的連結器,Id64,實現增量連結。這項任務相當複雜,所以我決定首先 profile 一下 Id64 的執行過程來尋找已有的實現方案。

Apple提供了Id64的C++原始碼,儘管是過時版本,我還是嘗試了一下,希望和XCode中包含的最新版本之間不要有根本性的差別。原始碼不是編譯好即時可用的,我參照Macports程式碼,快速地在 instrument 中執行起來Instruments)。

上面的截圖,也許你一眼看上去並不能獲得太多資訊,但是如果你讀過Id64的設計文件,你會迅速得出:ld::tool::Resolver::resolve()相當於Id64管道的Resolving 階段。簡言之,這個階段負責將表示各個目標檔案的圖表放到一個更大的全域性表裡面。

早些時候已經完成了目標檔案各自圖表的載入,因此,原則上,解析不會太費時,當然不應該影響執行時間(佔執行時間的76.9%)。因此,如果你仔細觀察,你會發現ld::tool::Resolver::linkTimeOptimize()佔用了51.3%的連結時間。

linkTimeOptimize()執行連結時優化(LTO)。在Clang/LLVM領域,這意味著連結器獲得的是LLVM位元組碼,而不是通常的目標檔案。這些位元組碼在一種更抽象的層次上代表程式的執行過程,允許LTO得以進行,但是壞處是,仍然需要將他們轉換成機器程式碼,在連結時需要額外的處理時間。

在Id64加入了一些logging做了一些枯燥的探索,我成功定位那些令人生厭的二進位制檔案。這些二進位制檔案是一個靜態庫的一部分。而這個靜態庫,出於善意,編譯優先順序設定為-O4。 Quoting Clang手冊記述如下:

-O4能夠優化連結時間;目標檔案以LLVM二進位制檔案格式儲存,在連結期間,優化了整個程式。

我將優先順序標誌降為-O3,然後連結器速度提高了51%,每個週期節約了大約15秒。

結果

每個週期大約縮短了25 + 15 = 40秒(佔等待時間的40 / 82 * 100 = 49 %)。我們大膽估算一下大約能節省多少時間:

假設:

  • 由於使用新的硬體,效率提高了2倍。
  • 開發者每10分鐘一個Edit-Build-Test週期。
  • 開發者每天工作5個小時。這樣每人每天可以節省 40 秒/ 2 (5 60 / 10) = 600 秒 = 10 分鐘。在一些大的 iOS 團隊像 Spotify,每10個iOS開發者,每天將會節省1小時40分鐘,完全不需要花費任何代價.這只是粗略估計,我們也要有所保留。我並沒有打算建立一個嚴格的測試對比。