2011-09-10 10 views
5

私は.parで遊んでいましたが、次の計算をパフォーマンスの向上のためにさらに並列化できるかどうか、または結果を高速に計算する他の方法があるかどうかは疑問です。私は最終結果がグループ化の順序に依存するとは思わないので、追加の可能性があることを期待しています。groupByを並列化する方法

それはこれを印刷し実行
object Test { 
    val data = (1 to 500000) map { i => (i % 100) -> (i % 10000) } 

    def mutableIndex = { 
    val map = collection.mutable.Map[Int, Set[Int]]().withDefaultValue(
     Set[Int]()) 
    for ((k, v) <- data) { map(k) = map(k) + v } 
    map 
    } 

    def immutableIndex = data.groupBy(_._1).map{ case (k, seq) => 
    k -> seq.map(_._2).toSet 
    } 

    def immutableParIndex = data.par.groupBy(_._1).map{ case (k, seq) => 
    k -> seq.map(_._2).toSet 
    } 

    def main(args: Array[String]) { 
    def bench(id: String)(block: => Unit) { 
     val times = (new testing.Benchmark { def run() = block }).runBenchmark(10) 
     println(id + " " + times + " sum: " + times.sum) 
    } 
    println("avail procs " + Runtime.getRuntime.availableProcessors) 
    bench("mutable"){ mutableIndex } 
    bench("immutable"){ immutableIndex } 
    bench("immutable par"){ immutableParIndex } 
    } 

} 

- 2.9.1を使用して:

$ scalac -d classes -optimize A.scala 
$ scala -cp classes Test 
avail procs 4 
mutable List(718, 343, 296, 297, 312, 312, 312, 312, 312, 312) sum: 3526 
immutable List(312, 266, 266, 265, 265, 265, 265, 265, 249, 265) sum: 2683 
immutable par List(546, 234, 234, 202, 187, 172, 188, 172, 187, 171) sum: 2293 

をいくつかの注意:

  • 上記の出力はかなりいいですが、パラレル版もはるかに応じて、矛盾しています私がdataで使用する定数と、benchで設定した繰り返しの回数(時には連続したものより効率が低い場合があります)を使用します。パラレルコレクションが期待されているのだろうか。
  • 私のベンチマークに欠陥がある場合は、セットが小さくなるにつれてセットが小さくなります(データの最後のモジュロを減らすことによって)
  • 私はそれを修正する方法を教えてください(例えば、すべての反復で同じデータを使用します。それは)結果をスキュー

編集:

def syncIndex = { 
    import collection.mutable.Builder 
    import java.util.concurrent.ConcurrentHashMap 
    import collection.JavaConverters._ 
    val m = new ConcurrentHashMap[Int, Builder[Int, Set[Int]]]().asScala 
    for ((k, v) <- data.par) { 
    val bldr = Set.newBuilder[Int] 
    m.putIfAbsent(k, bldr) match { 
     case Some(bldr) => bldr.synchronized(bldr += v) 
     case None => bldr.synchronized(bldr += v) 
    } 
    } 
    val b = Map.newBuilder[Int, Set[Int]] 
    for ((k, v) <- m) 
    b += ((k, v.result)) 
    b.result 
} 

それseee:ここバージョン同時ハッシュマップに基づいており、groupByのためのライブラリコードをモデルにしています2つのコアで素晴らしいスピードを出すためにはmsを使用してください。

答えて

2

あなたの質問には本当に答えはありませんが、.parは特にホットスポット(32ビット?)クライアントでスピードアップしますホットスポットサーバー。私はREPLでそれを走らせました。ベンチマークは、すでにウォーミングアップされているので、その後の実行でより速くなります。

私はタスクマネージャーごとにプロセッサーの使用量を監視しました。並列化されていないタスクでは約54%から並列化では75%になりました。

Java 7ではかなりのスピードが得られます。

Scalaバージョン2.9.0.1(Java HotSpot(TM)クライアントVM、Java 1.6.0_22)へようこそ。

scala> Test.main(Array[String]()) 
avail procs 2 
mutable List(1303, 1086, 1058, 1132, 1071, 1068, 1035, 1037, 1036, 1032) sum: 10858 
immutable List(874, 872, 869, 856, 858, 857, 855, 855, 857, 849) sum: 8602 
immutable par List(688, 502, 482, 479, 480, 465, 473, 473, 471, 472) sum: 4985 

scala> Test.main(Array[String]()) 
avail procs 2 
mutable List(1015, 1025, 1090, 1026, 1011, 1021, 1014, 1017, 1011, 1015) sum: 10245 
immutable List(863, 868, 867, 865, 864, 883, 865, 863, 864, 864) sum: 8666 
immutable par List(466, 468, 463, 466, 466, 469, 470, 467, 478, 467) sum: 4680 

Scalaのバージョン2.9.0.1(は、Java HotSpot(TM)64ビットサーバーVMはJava 1.6.0_22)へようこそ。

scala> Test.main(Array[String]()) 
avail procs 2 
mutable List(841, 360, 348, 338, 337, 338, 338, 342, 336, 336) sum: 3914 
immutable List(320, 303, 302, 300, 304, 302, 305, 299, 305, 299) sum: 3039 
immutable par List(521, 284, 244, 244, 232, 267, 209, 219, 231, 203) sum: 2654 

scala> Test.main(Array[String]()) 
avail procs 2 
mutable List(370, 393, 351, 342, 336, 343, 342, 340, 334, 340) sum: 3491 
immutable List(301, 301, 302, 305, 300, 299, 303, 305, 304, 301) sum: 3021 
immutable par List(207, 240, 201, 194, 204, 194, 197, 211, 207, 208) sum: 2063 

scala> Test.main(Array[String]()) 
avail procs 2 
mutable List(334, 336, 338, 339, 340, 338, 341, 334, 336, 340) sum: 3376 
immutable List(300, 303, 297, 301, 298, 305, 302, 304, 296, 296) sum: 3002 
immutable par List(194, 200, 190, 201, 192, 191, 195, 196, 202, 189) sum: 1950 

Scalaのバージョン2.9.0.1(は、Java HotSpot(TM)64ビットサーバーVMはJava 1.7.0)へようこそ。

scala> Test.main(Array[String]()) 
avail procs 2 
mutable List(763, 258, 227, 235, 238, 279, 245, 227, 227, 243) sum: 2942 
immutable List(274, 233, 228, 235, 238, 247, 243, 229, 233, 245) sum: 2405 
immutable par List(635, 303, 261, 258, 217, 291, 204, 248, 219, 184) sum: 2820 

scala> Test.main(Array[String]()) 
avail procs 2 
mutable List(229, 229, 229, 230, 234, 226, 227, 227, 227, 232) sum: 2290 
immutable List(228, 247, 231, 234, 210, 210, 209, 211, 210, 210) sum: 2200 
immutable par List(173, 209, 160, 157, 158, 177, 179, 164, 163, 159) sum: 1699 

scala> Test.main(Array[String]()) 
avail procs 2 
mutable List(222, 218, 216, 214, 216, 215, 215, 219, 219, 218) sum: 2172 
immutable List(211, 210, 211, 211, 212, 215, 215, 210, 211, 210) sum: 2116 
immutable par List(161, 158, 168, 158, 156, 161, 150, 156, 163, 175) sum: 1606 
+0

興味深い、私は1.7.0とのタイミングをしようとする必要があります。私は質問で同時ハッシュマップを使用してバージョンを追加しました。 2つのコアで高速でしたが、4で遅くなりました。私は1.7.0で何ができるのか分かりました。また、REPLでコンパイルされたコードを実行するよりも速い結果が得られることに気付きました! – huynhjl

+0

@huynhjl REPLを実行しているVMは、実行しているものにかかわらず、ある程度はウォームアップするので、新しいVMを起動して寒さからベンチマークを行うよりも速くなると思います。 REPL上でベンチマークを実行し、新しいTestオブジェクトを作成して実行することで、これを表示することができます。私にとっては、最初のインスタンスよりも時間がかかりました。また、VMの使用可能メモリを増やすことも試してください。 「古い」REPLで行った最初のベンチマークは、メモリー不足のエラーでクラッシュする前に、10倍遅くなっていました。 –

+0

新しい並行ハッシュトライを使用するように 'groupBy'実装を切り替える予定です。これはおそらく次のリリースで行われます。それはスケーラビリティを高めるはずです。 – axel22

0

一般的なアドバイスはmicrobecnhmarkingためキャリパーを使用することである。 https://github.com/sirthias/scala-benchmarking-template

また、時々parは初期構造のコピーを行うことに注意してください(少なくとも2.9.1で、https://issues.scala-lang.org/browse/SI-4984を参照)、例えば

`

scala> val data = (1L to 50000000) par (100) 
java.lang.OutOfMemoryError: Java heap space 
     at scala.math.Integral$class.mkNumericOps(Integral.scala:25) 
     at scala.math.Numeric$LongIsIntegral$.mkNumericOps(Numeric.scala:115) 
     at scala.collection.immutable.NumericRange.foreach(NumericRange.scala:75) 
     at scala.collection.Parallelizable$class.par(Parallelizable.scala:41) 
     at scala.collection.immutable.NumericRange.par(NumericRange.scala:42) 

`

+0

私のためにリンクが切れています。 – huynhjl

+0

'par'メソッドは、デフォルトの不変なマップのデータをコピーするべきではありません。' immutable.HashMap'は 'data'の型です。 – axel22