Xcode10に最適化したら、ビルド時間が3倍高速化した話

こんにちは、ココナラiOSアプリ開発担当の小林です。

以前のブログで、ココナラiOSアプリのビルド時間改善への取り組みを紹介しました。その後リリースされたXcode10では以下の変更が行われ、ビルド時間が大幅に改善されています。

  • Swiftコンパイラがバージョン4.2に対応
  • 新しいビルドシステム(New Build System)が正式対応

今回はXcode10対応とともに行ったビルド設定の最適化と、その成果について紹介したいと思います。

ビルド時間は下記の環境で計測を行っています。

  • MacBook Pro (15-inch, 2018)
    • 2.2 GHz Intel Core i7 (6-core)
    • 16 GB 2400 MHz DDR4
    • 256GB NVMe SSD
  • macOS Mojave 10.14.1
  • Xcode 9.4 / Xcode 10.1

Xcode9.4時点の状況

Xcode9.4ではあまりの差分ビルドの遅さから、いわゆる Whole Module Optimization を利用したビルド時間改善の設定を行って開発を進めており、このときのDebugビルド時間は次のとおりでした。

  • フルビルド: 122s
  • 差分ビルド: 56.8s (コード変更の大小に関わらず、ほぼ一定)

Releaseビルド(Archive)は以下のとおりほぼデフォルトの設定ですが、3490sとなんと1時間近くかかっています。

  • Whole Module Optimization
  • Optimization Level: 最適化(-O)を実施
  • Architectureは armv7/arm64
  • BitcodeをON

当初はBitriseでReleaseビルドを生成していたのですが、ビルド時間の上限である90分をオーバーしてしまい、利用できなくなるという事態に陥っていました😭

Xcode10対応の実施内容

Xcode10対応に際し、まずSwift4.2に対応するためのコンバート作業を行った後、いくつかの設定項目について見直しを行いました。

New Build Systemに最適化する

File メニューにある Workspace Settings... を選択すると、以下のようなシートダイアログが表示されます。

f:id:coconalainc:20181219181346p:plain

ここで Build System の設定が New Build System になっていれば、新しいビルドシステムが利用されます。

さらに、これまで Whole Module Optimization を利用してDebugビルドの時間改善を行っていた場合は、これを Incremental に戻します。

f:id:coconalainc:20181219181450p:plain

この設定に関する詳細は Building Faster in Xcode@WWDC 2018 にて言及されています。

最適化を-Osizeに変更する

Xcode9の頃も -Osize は指定できましたが、コンパイラがクラッシュする不具合があり採用できませんでした。Xcode10で改めて指定したところ問題はなさそうでしたので、Releaseビルドに採用しました。

f:id:coconalainc:20181219181554p:plain

Objective-Cのコードに対して最適化をかけたい場合は、 Apple Clang - Code Generation の配下にある Optimization Level を変更します。

最新のビルド時間

コンパイラのアップデートとビルド設定の見直しにより、Debugビルドの時間は以下のようになりました。

  • フルビルド: 77.6s
  • 差分ビルド: 12s〜40s (ファイルの変更内容は規模により変動)

特に差分ビルドが大幅に改善され、かなりストレスが軽減された印象です😇

またReleaseビルドについても 1044s と、従来の3倍☄️以上 高速化することができました!

ビルド中の負荷について

Releaseビルド中のCPUにかかる負荷を観察していると、次の傾向があることが分かりました。

  1. Whole Module Optimizationの前処理 (SIL optimizer)
    • 最大2プロセス(armv7およびarm64用)が稼動
    • 全体のビルド時間の中では僅か(2〜3分)
  2. コードコンパイルのメイン処理
    • CPUの論理コア数(6-core HT対応CPUなら12)分のプロセスが稼動
  3. Bitcodeへの変換
    • 最大2プロセスが稼動し続ける
    • 全体のビルド時間の大半を占める

"2"の段階においてはCPUリソースを使い切っていました。

f:id:coconalainc:20181219181633p:plain

一方"1"と"3"の段階ではTurbo Boostは発動しているものの、CPUリソースには余裕があるようです。

f:id:coconalainc:20181219181726p:plain

つまり、CPUコア数を増やした場合は"2"の高速化は期待できますが、"1"と"3"の高速化は、Turbo Boostのクロック数によることになります。 そして"3"の傾向から、Bitcodeを使用している場合、CPUコア数の多いMacを使用しても、ビルド時間が想定よりも改善しないこともありそうです。

まとめと課題

改善前後の結果をまとめると、以下のようになりました。

Before After 性能改善
Debugビルド(差分) 56.8s 12〜40s 1.4〜4.7x
Debugビルド(フル) 122s 77.6s 1.6x
Releaseビルド 3490s 1044s 3.3x

下記のポイントで、ビルド設定の見直すと良さそうです。

  • Xcode10でのDebugビルドはNew Build SystemIncrementalビルドの使用を推奨
  • AdHoc配信等で古い端末での検証が不要なら、arm64版のみをビルドする
  • Bitcodeの出力には時間を要し、CPUコア数の恩恵は少ない。急ぎの場合はOFFにすることも検討

Bitcode出力に関するログを観察すると、異様に時間のかかっているファイルがいくつかありました。コンパイラが苦手とするパターンがあるかもしれないので、さらに分析を進めれば、よりビルド時間を改善することができそうです。

正直なところ、Xcode10への移行とビルド設定を見直すだけでこれだけビルド時間が改善されるとは想像していませんでした。「金の弾丸」を導入するのも有効ではありますが、まず自プロジェクトの傾向を分析し、ソフトウェアレベルでの改善を実施してみてはいかがでしょうか。