2016年11月17日木曜日

ビルドが遅いのをどうにかこうにかやりくりする方法

すでに皆様痛感されていると思いますが、Swiftはビルドがとんでもなく遅いです。正確に言うと、Swiftのビルドが遅いのに加え、clang moduleのビルドが遅いため、frameworkの数が増えてくるとビルドにかかる時間がどんどん破滅的なことになってきます。

Swift & dynamic framework導入前はアーカイブ込みで10分以下だったビルド時間が、現在は20分を超えてしまっており、更に今後もSwiftコードの増加ととframeworkの増加に従ってビルド時間が伸びることが予想されます。そこでなんとかしてこの時間を短縮しようと思って調べてみました。

そもそもビルド時間が伸びる原因


  • Swiftのビルドが遅い、特に型推論が遅い
  • frameworkのビルドが遅い
  • Xcode上でcleanを実行するとプロジェクト内でビルドしているのframeworkが全てビルドし直しになり分割ビルドの恩恵が得られない

それぞれ詳しく見ていきます。

Swiftのビルドが遅い、特に型推論が遅い

以下の記事が大変参考になります。
https://thatthinginswift.com/debug-long-compile-times-swift/
Swift 3.0になって随分と高速化したため、型推論のせいで異常に時間がかかるという自体は減りましたが、それでもObjective-Cのコードと比べると10倍以上時間がかかることが多々あります。型を明示すれば高速化に寄与できますが、そうするとSwiftのせっかくの旨味が減るため悩ましいところです。

frameworkのビルドが遅い

dynamic framework = clang moduleのビルドはコードのコンパイル以上の作業を多分に含むため、分割すれば分割するほどそのオーバーヘッドが増えて無駄に時間がかかることになります。

Xcode上でcleanを実行するとプロジェクト内でビルドしているのframeworkが全てビルドし直しになり分割ビルドの恩恵が得られない

個人的に最悪なのがこれだと思います・・・せめて特定のコードに対してのみcleanできれば良いのですが、Xcodeで各種トラブルを避けるためにはcleanは必須、しかしながらcleanを実行すると全てのframeworkがビルドし直しとなって分割ビルドの恩恵が得られません。

特にCocoaPodsを使っていると全てのpod管理下のframeworkが再ビルドとなるため大変効率が悪いです。

これらを踏まえて対処法を考えてみました。と言っても大したことは出来ないのですが、

  • 定期的にビルドタイムをチェックする環境を用意する
  • CocoaPodsをやめてcarthageを使用する
  • microframeworkをやめる、どうしてもmicroframeworkを行うのであればcarthageと併用する

ビルドタイムのチェック方法は https://thatthinginswift.com/debug-long-compile-times-swift/ に記載がありますが、Other Swift Flags-Xfrontend -debug-time-function-bodiesを追加してビルドし、ビルドレポートを見ればOKです。結果はpbpaste | egrep '\.[0-9]ms' | sort -t "." -k 1 -n | tail -10などで整形すると見やすくなります。100msを超えている関数があれば手を打つ必要があるかもしれません。

CocoaPodsですが、手元で全てのframeworkをビルドしようとするためcleanした際の無駄が多いです。carthageを使えば最初にビルドを一回行って後はそれを参照して使いまわすだけ、という風にできます。ただしcarthageは最初のビルドがやたら長く、CocoaPodsを使った場合より5倍以上遅い?と言う問題があるのでその点は注意が必要です。

microframeworkを辞める論についてはおそらくいちばん賛否両論議論があるところかと思いますが、少なくとも殆どの場合、開発中はコードを使いまわす回数と時間よりXcodeをcleanしてビルドし直す回数と時間のほうが支配的であると信じています。Xcodeがちゃんとしてさえいればベストプラクティスに従ったほうが良いのでしょうが、そのようなことは伝統的にないので、ベストプラクティスにこだわらずビルドを高速化するために一つのframeworkにまとめるのも悪くはないのではと思っています。
ただしcarthageと併用できるのであれば、一度ビルドしたmicroframeworkを使いまわすメリットが享受できるので良いかもしれません。その場合、microframework側はコードベースがある程度安定している必要があります。さもなければ何度も何度もcarthage updateを走らせ直して結局時間がかかることになります。

結論

いろいろ考えてみましたが、頑張ってビルド高速化するより、新しいビルドマシンを増設したほうが結果的に良いかと思います\(^o^)/

2016/12/01 追記

Qiitaにより詳細で素晴らしいまとめがあったのでそっちを見ておいたほうが良い気がします(´・_・`)

全体的に見て型キャストとかワンライナー(とそれを誘発する演算子等)による途中の型推論が劇的に遅い原因のようですね。まぁ来年ぐらいまでにはclangが賢くなって直ってるのではないでしょうか・・・多分・・・