同じ処理結果を求めるだけでも、コードの書き方によって処理速度が変わるのは皆さんもご存知の通りだと思います。
今回、処理速度を求めることは目的としていません。
コンパイル時間が遅くて大変な思いをすることの多いSwiftについて、コードの書き方によってどの様にコンパイル時間が変わるかやってみました。
環境は
MacBook Pro Retina 15-inch late 2013 Corei7 memory 16GB
MacOS Sierra
Xcode 7.3
です。
Swiftのコンパイル時間については、よくDictionary型の宣言方法などが挙げられています。
また、データ型のキャストもコンパイル時間に影響を与えているようです。
今回それらとは別に、主に配列のデータ取り出しに注目してやってみました。
ものによっては、こんなの当たり前だよ!というケースもあるかもしれませんが...その点はご容赦ください。
今回は、ある配列の特定の範囲の総和を求める計算を行いたいと思います。(特に理由はありません)
Flag の追加
まず、コンパイルにかかる時間の計測方法です。
Build Setting -> Swift Compiler -> Custom Flags -> Other Swift Flagsに以下を追加してください。
-Xfrontend -debug-time-function-bodies
こうすることで、Xcodeが関数ごとのコンパイル時間をログに残してくれる様になります。
コード
以下、多少強引ですが、色々なケースで書きうるコードをいくつか作って試してみました。
引数の配列の、0th indexから3rd indexまでの値の総和を求める関数です。
//index番号が自明な場合で、連続した4つの値を計算する場合
func accumulation(array:[Float])->Float
{
if(array.count > 4){
return array[0] + array[1] + array[2] + array[3]
}else{
return 0
}
}
//index番号が自明ではなく、あるindexから連続した4つの値を計算する場合
func accumulation2(array:[Float])->Float
{
if(array.count > 4){
let i:Int = 0
return array[i] + array[i+1] + array[i+2] + array[i+3]
}else{
return 0
}
}
//indexが自明な場合で、for loopを使用して計算する場合
func accumulation3(array:[Float])->Float
{
if(array.count > 4){
var a:Float = 0
for i in 0..<4{
a += array[i]
}
return a
}else{
return 0
}
}
//indexが自明で、クロージャを使用する場合
func accumulation4(array:[Float])->Float
{
if(array.count > 4){
return array[0...3].reduce(0){$0+$1}
}else{
return 0
}
}
これでCompileします。
計測結果
計測結果はReport Navigator の Build欄から確認できます。
そsいて、 Compile Swift source filesの、Compile main.swiftの行、左にあるアイコンをクリックして、コンパイルの詳細を表示させます。
右端にミリ秒単位で、関数ごとのコンパイル時間が表示されているのがわかります。
画像では、上から
accumulation()
accumulation2()
accumulation3()
accumulation4()
と表示されています。
コンパイル時間は、for loopを使用したケースが最も短いことがわかりました。
Swift では i++ などという記述はもうできませんが、C言語などでは配列のindexに i++や++iを書くことはよくありますよね。こういう手法はSwiftのコンパイルをとても遅くしている可能性があります。
皆さんの環境ではいかがでしょうか。
indexを足し算する方法では、なんと2秒以上もコンパイル時間がかかっていました...
効率的な計測結果閲覧方法
上記の計測結果の欄を command + a で全て選択し、command + cでクリップボードにコピー。
そのままの状態でTerminal上で
pbpaste | egrep '.[0-9]ms' | sort -t "." -k 1 -n | tail -10
のコマンドを実行すれば、計測時間の大きい関数順にsortされます。
上のコマンドでは、表示件数は10件です。
参考 : https://twitter.com/erikaderstedt/status/725217977314992128?ref_src=twsrc%5Etfw
他にも、様々なコードで書き方一つでコンパイル時間が大きく異なることがあるかもしれません。