2017-10-29 6 views
1

ループ内のIndexedSeqを構築するにはArrayBufferを使用し、続いて ".toVector()"を使用してVectorに変換する必要があります。Scala:IndexedSeq.newBuilderとArrayBuffer

プロファイリングされた例では、このセクションにCPUホットスポットがあることを示していました。代わりに、IndexedSeq.newBuilder()を使用し、続いて ".result()"で不変に変換しました。

この変更により、パフォーマンスが大幅に向上しました。コードはほとんど同じです。したがって、IndexedSeq.newBuilder()を使用するのがベストプラクティスであるようです。これは正しいです?以下の例のメソッドは、ArrayBufferの違いをコメントアウトして示しています。

def interleave[T](a: IndexedSeq[T], b: IndexedSeq[T]): IndexedSeq[T] = { 

    val al = a.length 
    val bl = b.length 

    val buffer = IndexedSeq.newBuilder[T] 
    //---> val buffer = new ArrayBuffer[T](al + bl) 
    val commonLength = Math.min(al, bl) 
    val aExtra = al - commonLength 
    val bExtra = bl - commonLength 

    var i = 0 
    while (i < commonLength) { 
    buffer += a(i) 
    buffer += b(i) 
    i += 1 
    } 

    if (aExtra > 0) { 
    while (i < al) { 
     buffer += a(i) 
     i += 1 
    } 
    } else if (bExtra > 0) { 
    while (i < bl) { 
     buffer += b(i) 
     i += 1 
    } 
    } 

    buffer.result() 
    //---> buffer.toVector() 
} 
+0

「ArrayBuffer」自体を返さないのはなぜですか? 'IndexedSeq'を実装しています。 –

+0

@AlexeyRomanov 'ArrayBuffer'は_mutable_' IndexedSeq'を実装しています。_OP_は_immutable_バージョンを返すようです。 –

+0

'scala.collection.IndexedSeq'を返すと、(キャストやパターンマッチングなしで)それを変更することはできません。その敵対的なコードやひどく書かれたコードでは安全ではありませんが、これは重要な問題ではありません。 –

答えて

0

この例では、非公式テストは欺いていませんでしたが、ScalaMeterはパフォーマンスをより明確に示しています。 ArrayBuffer(上のオレンジ色の線)で結果を構築することは、より直接的なnewBuilder(青色の線)よりもはるかに遅いです。

ArrayBufferをIndexedSeqとして返すことは、最も速い(緑色の線)ですが、もちろん、不変のコレクションの真の保護を与えるものではありません。

中間結果をArray(赤い線)に構築するのは、ArrayBufferとnewBuilderの中間です。

ScalaMeter Offline Report

「zipAll」収集方法は、インターリーブは、より機能的なスタイルで行うことができるようになります

def interleaveZipAllBuilderPat[T](a: IndexedSeq[T], b: IndexedSeq[T]): IndexedSeq[T] = { 
    a.zipAll(b, null, null).foldLeft(Vector.newBuilder[T]) { (z, tp) => 
     tp match { 
     case ((x:T, null)) => z += x 
     case ((x:T,y:T)) => z += x += y 
     } 
    }.result() 
    } 

最も遅いが、上の2つはほぼ同じとこれらと機能的方法、ありパターンマッチを行うという点と、if文を使うという点だけが異なるので、パターンは遅くありません。

ArrayBufferを使用して結果を累積すると機能がわずかに低下しますが、newBuilderを使用するダイレクトループは大幅に高速です。

「zipAllは」ビルダーを返すことができ、かつ場合ビルダーは反復可能だった、機能的なスタイルがより速くなることができなかった場合 - 次のステップは、単に要素の反復処理を必要とする場合は不変の結果を生成する必要。

Functional style performance

だから、私のためnewBuilderは明確な勝者です。

3

のベストプラクティスである、私はそれはあなたの要件に依存推測する通り。どちらの方法も容認でき、理解できるものです。すべてのことが等しい、この特定のケースでは、私はArrayBuilder(の作成をターゲットにしているので、前者の結果はVectorです)よりIndexedSeq.newBuilderを好むでしょう。

ベンチマーク上のジャストワンポイント:これは本当の芸術形式であるが、原因などのキャッシング、JIT & のHotSpotパフォーマンス、ガベージコレクションにあなたがこれを行うために使用して検討するかもしれないソフトウェアのワンピースはScalaMeterです。最終的なベクトルを取り込むためには、関数の両方のバージョンを書く必要があります。ScalaMeterは、両方について正確な統計を提供します。 ScalaMeterは、測定前にウォームアップにコードを許可し、CPU時間だけでなくメモリ要件も調べることができます。

関連する問題