私は.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を使用してください。
興味深い、私は1.7.0とのタイミングをしようとする必要があります。私は質問で同時ハッシュマップを使用してバージョンを追加しました。 2つのコアで高速でしたが、4で遅くなりました。私は1.7.0で何ができるのか分かりました。また、REPLでコンパイルされたコードを実行するよりも速い結果が得られることに気付きました! – huynhjl
@huynhjl REPLを実行しているVMは、実行しているものにかかわらず、ある程度はウォームアップするので、新しいVMを起動して寒さからベンチマークを行うよりも速くなると思います。 REPL上でベンチマークを実行し、新しいTestオブジェクトを作成して実行することで、これを表示することができます。私にとっては、最初のインスタンスよりも時間がかかりました。また、VMの使用可能メモリを増やすことも試してください。 「古い」REPLで行った最初のベンチマークは、メモリー不足のエラーでクラッシュする前に、10倍遅くなっていました。 –
新しい並行ハッシュトライを使用するように 'groupBy'実装を切り替える予定です。これはおそらく次のリリースで行われます。それはスケーラビリティを高めるはずです。 – axel22