iOSエンジニアの皆さん、ゴリゴリSwift書いていますか?サクサク書ける反面、コンパイルにやたら時間がかかってストレスフルですよね。今回は、激遅だったコンパイル時間が「ちょい遅」くらいまで改善したお話です。
「ナウでヤングなiOSエンジニアはやっぱSwiftだよね!」ということで、半ば強引にSwiftを採用して直近のプロジェクトを進めていました。
補完機能が頻繁に落ちたり、ブレークポイントの位置がおかしかったり、変数の中身が見られなかったり、謎のエラーでビルドできなかったり、…などなど、まだまだバグはてんこ盛りですが、それらを見なかったことにできる程度にはいい感じです。型推論はそこそこ賢いし、何より簡潔に記述できます。ただの可変長配列のために、もうNSMutableArrayなんて長々と書かなくてもええんやで。
プロジェクトが進んでソースコードが増えてくると、とある問題が気になってきます。
「ソースのビルドが遅い。すごく時間がかかる」
はじめのほうは我慢していましたが、プロジェクトも終盤に差しかかると1回のビルドで2分以上かかる始末。これではMac Proが何台あっても足りません。
Swiftのコンパイルはなぜ遅いのか?
1つ目は、現在のXcode(6.2)では差分コンパイルがサポートされていないこと。ビルドの様子を眺めていると分かるのですが、1ファイル変更しただけでも全体の再コンパイルがかかっています。
2つ目は、おそらく型推論。Objective-Cと異なり、Swiftではコンパイラが自動的にある程度の型推論をしてくれます。書かれていない情報を計算するわけで、ObjCと比べるとその分コンパイル時間が増えるというのは直感的に理解できます。
それにしても、100弱のクラスを含むプロジェクトで2分もかかるのは、いくら何でも遅すぎます。何でこんなに遅いんだろう?ビルドの様子を眺めていると…あることに気がつきます。
ひとつだけ、異様に時間のかかるクラスがある。
コンパイル時間の大半が、このクラスに割かれていることが分かりました。いったいどれだけ複雑なクラスなんだろうか?
ところが、いざファイルを開いてみると…ちっさ!予想に反して、100行未満のとても小さなクラスでした。変数(定数)も含まれていますが、さほど複雑ではないものです。
// 容疑者クラスに含まれる変数のイメージ図
private let kSomeConstant = [
["a", "b"],
["c", "d"],
["e", "f"],
["g", "h"],
["i", "j"],
["k", "l"],
["m", "n"],
["o", "p"],
["o", "c"],
["o", "c"],
["u", "v"],
["w", "x"],
["y", "z"],
]
ここで自分の直感がささやきます。
「この一見単純な変数の型推論に、膨大な時間を割いているのではないか?」
早速検証!さきほどの変数宣言に型を足してあげます。ほらほらSwiftさま、型情報だよー。
private let kSomeConstant: [[String]] = [
["a", "b"],
["c", "d"],
["e", "f"],
["g", "h"],
["i", "j"],
["k", "l"],
["m", "n"],
["o", "p"],
["o", "c"],
["o", "c"],
["u", "v"],
["w", "x"],
["y", "z"],
]
すると…。あんなにかかっていたコンパイル時間が、30秒ほどで完了するではありませんか!たった1つ型情報を追加してあげるだけで、75%も短縮されました。
ということで、Swiftのコンパイルが遅すぎるときは、コンパイルの過程を眺めてみると改善のヒントが見つかるかもしれない、というお話でした。異様に時間のかかるクラスがあったら、型の省略を見直してみるとよいかもしれません。