Qiitaにも同様の投稿をしています。
http://qiita.com/you_matz/items/e95f30023eccc8d96357
2016年11月現在、最新(Xcode8.1環境下での)のコンパイル時間の計測方法が見当たらないので分析方法まで調査した。
2分程度かかっていたビルドが10秒ほどに短縮できました。
※ビルドするマシンのスペック、設定、ファイル数、コードの書き方にもよるので、
n%,n秒速くなったというのは相対的な値なので予めご了承ください。
http://qiita.com/you_matz/items/e95f30023eccc8d96357
2016年11月現在、最新(Xcode8.1環境下での)のコンパイル時間の計測方法が見当たらないので分析方法まで調査した。
2分程度かかっていたビルドが10秒ほどに短縮できました。
※ビルドするマシンのスペック、設定、ファイル数、コードの書き方にもよるので、
n%,n秒速くなったというのは相対的な値なので予めご了承ください。
はじめに
100クラス弱のswiftのプロジェクトで2分強ビルドに時間がかかっていたので、おかしいなと思い、おそらく静的にベタ書きした多次元配列に型情報を与えていないからだろうなと分かっていたが、いい機会なので原因を調査してみた。
プロジェクトのビルド時間の計測
こちらを参考に(Swiftのメソッド毎のコンパイル時間を計測してビルド時間を短縮する)
単にビルドが遅いと言われても、実質どのくらい時間がかかっているかわからないので計測
コンソールで以下実行
単にビルドが遅いと言われても、実質どのくらい時間がかかっているかわからないので計測
コンソールで以下実行
defaults write com.apple.dt.Xcode ShowBuildOperationDuration YES
これくらいビルド時間が速いと気にしなくていいですが、ビルド時間が遅い場合は以下で計測
ファイル毎のコンパイル時間の計測
まずxctoolで試す(※Xcode8でxctoolのbuildコマンドは使えなくなっている)
xctoolでファイル毎のコンパイル時間、メソッド毎のコンパイル時間を計測しようとしたが使えない、、、(xctoolでのbuildはXcode7だけ)
https://github.com/facebook/xctool#building-xcode-7-only
https://github.com/facebook/xctool#building-xcode-7-only
普通にxcodebuildで
プロジェクトルートにて以下コマンド
xcodebuild -workspace YOUR-APP.xcworkspace/ -scheme YOUR-APP clean build OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-function-bodies" | grep .[0-9]ms | grep -v ^0.[0-9]ms | sort -nr > buildResult.txt
clean build、
OTHER_SWIFT_FLAGS
に -Xfrontend -debug-time-function-bodies
を追加することでメソッド、プロパティ毎に計測可能となるYOUR-APP
は適宜変更してください。cocoapods使用の場合は-workspace
ですが、していない場合
-project YOUR−APP.xcodeproj/
必要な行だけ抽出して、コンパイルに時間を用するファイル、メソッド、プロパティ順でソート
結果その1(全ファイル)
pods内コンパイル時間も入っているけど、1ファイルだけで7.6秒はやばい、、
Build Time Analyzer for Xcodeで計測(こちらがおすすめ)
Xcode8になってからAlcatraz(PackageManager)が使えないので、そのままソースからビルドしてMacアプリを起動
ソースはこちらから
https://github.com/RobertGummesson/BuildTimeAnalyzer-for-Xcode/releases
ソースはこちらから
https://github.com/RobertGummesson/BuildTimeAnalyzer-for-Xcode/releases
アプリを起動すると以下のウィンドウが立ち上がる
instructionsに沿って、
Build Settings > Swift Compiler - Custom Flags
Build Settings > Swift Compiler - Custom Flags
OTHER_SWIFT_FLAGS
に-Xfrontend -debug-time-function-bodies
を追加- クリーン
- ビルド
- 以下のようなファイル、メソッド毎のコンパイル時間が表示された画面が起動
結果その2(GUI)
サンプルプロジェクトでの結果なので3秒とすぐです。結果1とは違いPods内は含まれません。
行選択でファイルまで飛ぶことができ、
これで、どのファイルのどの箇所がコンパイルするのに時間がかかるのか予測がつきます。
行選択でファイルまで飛ぶことができ、
per file
にチェックでファイルごとの時間もわかるこれで、どのファイルのどの箇所がコンパイルするのに時間がかかるのか予測がつきます。
カスタムフラグを追加
2016/12/11追記 swift3.0以降で有効。カスタムフラグを追加
Build Time Analyzerを起動する必要がないので現時点ではこちらの方が便利かもしれません。
Build Time Analyzerを起動する必要がないので現時点ではこちらの方が便利かもしれません。
Build Settings > Swift Compiler - Custom Flags
OTHER_SWIFT_FLAGS に
これによってコンパイルに100ms以上かかっている箇所をwarningで出してくれます。
以下のような表示です。
OTHER_SWIFT_FLAGS に
-Xfrontend
と -warn-long-function-bodies=100
を追加これによってコンパイルに100ms以上かかっている箇所をwarningで出してくれます。
以下のような表示です。
5249msタイプチェックにかかっているとのこと、クリックして修正箇所に飛べます。
1秒であれば1000, 0.5秒であれば500にしてもいい。
これらのオプションは将来的には予告なしにサポートされなくなる可能性もあります。
-warn-long-function-bodies=100
の100はワーニングを出すかの敷居値なので、1秒であれば1000, 0.5秒であれば500にしてもいい。
これらのオプションは将来的には予告なしにサポートされなくなる可能性もあります。
改善策
簡単にできるものから
※マシンの性能上げろとか買い換えろとか単純な策は誰でも思いつくのでなしです
※マシンの性能上げろとか買い換えろとか単純な策は誰でも思いつくのでなしです
設定レベル-事前にできること
Optimization Levelを見直す
Build Settingの中にApple LLVM8.0 - Code GenerationとSwift Compiler があります。
Optimization Levelがデフォルトで
まれに
Optimization Levelがデフォルトで
Debug None[-Onone]
となっていますが、まれに
Fast
に設定している場合があるのでこれをDebug時はNone
とすることで最適化が行われず、ビルド時間が短くなります。リリースビルド用ではfast
,Fast, Whole Module Optimization
指定して最適化が行われるようにするべきです。User-DefinedにSWIFT_WHOLE_MODULE_OPTIMIZATIONを追加
Build Setting > Add User-Defined Settingに以下を追加
最適化がかかる場合、ビルド時間が半分程に短縮できました。
リリース用ビルドでは最適化がかかるのでテストサーバとか配布用サーバなどでこの設定は有効です。
SWIFT_WHOLE_MODULE_OPTIMIZATION = YES
最適化がかかる場合、ビルド時間が半分程に短縮できました。
リリース用ビルドでは最適化がかかるのでテストサーバとか配布用サーバなどでこの設定は有効です。
コンパイルの並列化
コア数に応じて、同時実行数を指定
defaults write com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks 2
これでビルド時間が半分以下に縮む
Pods内コンパイルをスキップ
これはあまりおすすめできませんが紹介しておきます。
- Product->Scheme->Edit Schemeでスキーマの編集画面へ
- 左のリストからBuildを選択し、右画面のFind Implicit Dependenciesのチェックを外す
※Pods内を変更してもビルドが走らないので注意
※Find Implicit Dependanciesにチェックをつけてクリーン、ビルド
※Find Implicit Dependanciesにチェックをつけてクリーン、ビルド
CocoaPodsをCarthageに切り替える
Carthage対応のライブラリはPodsでインストールしない方がよいです
- 事前にフレームワークを作成できるのでコンパイルなしでその分コンパイル時間を短縮できる
- つまりPodsだとクリーンインストール毎にビルドし直す必要があるが、Carthageはビルドし直す必要がない
コードレベル
型推論させない
明示的に型を付与させる
例えば以下のようなDictionaryを型情報なしで書くと型推論が働いて、コンパイルに時間がかかるので、
例えば以下のようなDictionaryを型情報なしで書くと型推論が働いて、コンパイルに時間がかかるので、
型を付与
配列の結合は以下のように
配列内容にもよりますが、コンパイルに時間がかかる可能性があります。
文字列連結で複雑な連結の仕方をしている場合も同様。
文字列連結で複雑な連結の仕方をしている場合も同様。
または+で連結するよりappendで追加
??演算子は使用しない
これはだめ
はじめの複雑な連結はやめて、適切にアンラップ
メソッド内、ビルド時間を99.4%削減
メソッド内、ビルド時間を99.4%削減
アンラップの箇所は
コード的にも読みやすいというのはコンパイラも解釈しやすいということでしょうか
guard let
でもよいコード的にも読みやすいというのはコンパイラも解釈しやすいということでしょうか
三項演算子もだめ
92.9%削減
Closureの引数の型を明示する
上記の例の用に$0でアクセスできますが、型推論が働いてしまうので明示的に型を付与
多重キャスト
普通はこんなことしないと思いますが、
驚くべきごとに、うっかりミスのこの箇所だけで3.4秒かかってしまっていた。
CGFloat
をCGFloat
にキャストしたり、驚くべきごとに、うっかりミスのこの箇所だけで3.4秒かかってしまっていた。
lazy properties
Swift2.2では遅延プロパティをタイプチェックし、
ターゲット内のすべての単一の.swiftファイルの遅延プロパティのコンパイル時間は累積されます。Swift 3.0では、この問題は引き続き起こりますが、ビルド時間はほぼ半減しています。
遅延プロパティを使用している場合は、注意を払うことをお勧めします。
ターゲット内のすべての単一の.swiftファイルの遅延プロパティのコンパイル時間は累積されます。Swift 3.0では、この問題は引き続き起こりますが、ビルド時間はほぼ半減しています。
遅延プロパティを使用している場合は、注意を払うことをお勧めします。
ビルド時間を改善するには、できるだけプライベートメソッドにコードを移動
上記の遅延プロパティは繰り返しタイプチェックを受け取りますが、コードを移動するとビルド時間が96.7%短縮されます。
参考: Swift build time optimizations — Part 2
https://medium.com/swift-programming/swift-build-time-optimizations-part-2-37b0a7514cbe#.pshxh3hjo
参考: Swift build time optimizations — Part 2
https://medium.com/swift-programming/swift-build-time-optimizations-part-2-37b0a7514cbe#.pshxh3hjo
final
, private
修飾子を付与
動的ディスパッチを減らすため(実行時のオーバーヘッドをなくし、パフォーマンスをあげる)
継承しないクラス、メソッド、プロパティに関しては
間接実行ではなく直接実行するので速い
継承しないクラス、メソッド、プロパティに関しては
final
, private
修飾子を付与間接実行ではなく直接実行するので速い
- overrideされることがなければ、直接実行されるので高速化
- 適切に
public
internal
final
private
を使い分ける
Struct
, Enum
を使用
Struct
は継承できず、直接実行なので速いstruct
とenum
を組み合わせることで継承クラスのような振る舞いは可能class
はヒープ領域に、struct
はスタック領域にメモリ確保されるので速いスタックはポインタ加算でメモリ確保、減算でメモリ破棄、シンプル
ヒープは「空き」を探しメモリ確保する、その領域に再挿入することでメモリ破棄
Heapは様々なスレッドからアクセスされるので「保護」する必要がある
このコストは決して小さくはない
最後に
型推論をさせない点で言えば、静的な型定義は高速化のポイントになりますが、冗長になり可読性が落ちるということもありえますので、分析結果をみてパフォーマンスが悪い部分のみ、型宣言するのでもいいかもしれません。
ビルド時間が遅いのはいらいらしますし、何よりコンパイラを混乱させないように、解釈しやすいようにコードを書くことはビルド時間の短縮、可読性を上げることにも繋がるのでおすすめです。Swift3.0でパフォーマンスが上がったことは間違いないですが、コンパイラにとって何が解釈しやすいかを調べることはこれからも重要でしょう。
ビルド時間が遅いのはいらいらしますし、何よりコンパイラを混乱させないように、解釈しやすいようにコードを書くことはビルド時間の短縮、可読性を上げることにも繋がるのでおすすめです。Swift3.0でパフォーマンスが上がったことは間違いないですが、コンパイラにとって何が解釈しやすいかを調べることはこれからも重要でしょう。
参考
Profiling your Swift compilation times
http://irace.me/swift-profiling
http://irace.me/swift-profiling
Regarding Swift build time optimizations
https://medium.com/@RobertGummesson/regarding-swift-build-time-optimizations-fc92cdd91e31#.fvpq4qkxw
https://medium.com/@RobertGummesson/regarding-swift-build-time-optimizations-fc92cdd91e31#.fvpq4qkxw
XCode 8.0 Swift 3.0 slow indexing and building
http://stackoverflow.com/questions/39547197/xcode-8-0-swift-3-0-slow-indexing-and-building
http://stackoverflow.com/questions/39547197/xcode-8-0-swift-3-0-slow-indexing-and-building
Swiftのメソッド毎のコンパイル時間を計測してビルド時間を短縮するhttp://qiita.com/rizumita/items/913b05d799b3712260f6
【Swift】 それ、enumとstructでやってみましょう!!
http://www.slideshare.net/uin010/swift-enum
http://www.slideshare.net/uin010/swift-enum