私はSwiftにパフォーマンスクリティカルなコードを書いています。私が考えることのできるすべての最適化を実装し、Instrumentsのアプリケーションをプロファイリングした後、Floatの配列に対してCPUサイクルの大部分がmap()
とreduce()
の演算を実行するのに費やされることに気付きました。ですから、私はmap
とreduce
のすべてのインスタンスを古き良きのfor
ループに置き換えました。と私の驚きに... for
ループははるかに、はるかに速かった!Swiftパフォーマンス:map()とreduce()vs forループ
これに少し戸惑って、私はいくつかのラフなベンチマークを行うことにしました。
// Populate array with 1,000,000,000 random numbers
var array = [Float](count: 1_000_000_000, repeatedValue: 0)
for i in 0..<array.count {
array[i] = Float(random())
}
let start = NSDate()
// Construct a new array, with each element from the original multiplied by 5
let output = array.map({ (element) -> Float in
return element * 5
})
// Log the elapsed time
let elapsed = NSDate().timeIntervalSinceDate(start)
print(elapsed)
と同等for
ループの実装:20.1秒:map
ため
var output = [Float]()
for element in array {
output.append(element * 5)
}
の平均実行時間つのテストでは、私はそうのようないくつかの簡単な計算を実行した後にfloatの配列を返しmap
ました。 for
ループの平均実行時間:11.2秒結果は、浮動小数点数の代わりに整数を使用して同様でした。
Swiftのreduce
のパフォーマンスをテストするために同様のベンチマークを作成しました。今回、reduce
とfor
ループは、1つの大きな配列の要素を合計するとほぼ同じ性能を達成しました。しかし、ときに、このようなIループテストを10万回:対
// Populate array with 1,000,000 random numbers
var array = [Float](count: 1_000_000, repeatedValue: 0)
for i in 0..<array.count {
array[i] = Float(random())
}
let start = NSDate()
// Perform operation 100,000 times
for _ in 0..<100_000 {
let sum = array.reduce(0, combine: {$0 + $1})
}
// Log the elapsed time
let elapsed = NSDate().timeIntervalSinceDate(start)
print(elapsed)
:for
ループは(明らかに)0.000003秒かかりながら
for _ in 0..<100_000 {
var sum: Float = 0
for element in array {
sum += element
}
}
reduce
方法は29秒かかります。
もちろん、私はコンパイラの最適化の結果として最後のテストを無視する準備はできていますが、コンパイラがループとSwiftの組み込みの配列メソッドに対して異なる方法で最適化する方法についていくつかの洞察を与えるかもしれません。すべてのテストは、2.5 GHz i7 MacBook Proで-O最適化を使用して実行されたことに注意してください。結果は配列サイズと反復回数によって異なりますが、for
ループは他の方法よりも常に1.5倍以上、時には10倍以上パフォーマンスが優れています。
ここで私はスウィフトのパフォーマンスについて少し難解です。そのような操作を実行するための単純なアプローチよりも組み込みのArrayメソッドが高速であるべきではありませんか?おそらく私よりも低レベルの知識を持つ誰かが、その状況を明らかにすることができます。
おそらく、最後の例では、集計の結果がまったく使用されず、ループ全体が削除されることをコンパイラは認識しています。ループの後に合計を出力すると、違いが生じるはずです。 –
良いアイデア - 間違いなく遅くなりました。正直なところ、print()を呼び出す私の経験では、とにかく何度も非常に遅いので、どのような違いがあるのかは分かりません。これは2つのメソッドの最適化の違いの良い例ですが、reduce()ループについても同じ結論を出すように思えます。 – hundley
この記事では、for-loopsとreduceのパフォーマンスの違いについていくつかの情報を提供しています。http://airspeedvelocity.net/2015/08/03/arrays-linked-lists-and-performance/ – JDS