3

並行して処理しようとしている xml ファイルがたくさんあります。future を使用する私の scala コード (2.9.2) は、最初はうまくいきますが、マシンにある 32G のほぼ 100% を消費してしまいます。これをシーケンシャルに行うと発生しないので、scala フューチャーを使用する際のガベージ コレクションに何か問題があると思います。

これが私のコードの簡素化されたバージョンです。誰が何が悪いのか教えてもらえますか?

val filenameGroups = someStringListOfFilepaths.grouped(1000).toStream
val tasks = filenameGroups.map {
  fg =>
    scala.actors.Futures.future {
      val parser = new nu.xom.Builder() // I'm using nu.xom. Not sure it matters.
      fg.map {
        path => {
          val doc = parser.build(new java.io.File(path))
          val result = doc.query(some xpath query)
          result
        }
      }.toList
    }
}

val pairs = tasks.par.flatMap(_.apply)

ETA: わかりました、これは解決しましたが、なぜこれが違いを生むのかまだわかりません.

内側のループのほとんどのコードを抽象化し、再実行しました。そして、未来からパーサーのインスタンス化を引き出しました。メモリ使用量は、まともな 17% で横ばいになりました。なぜこれが違いを生むのか、誰にも分かりますか?

これが私がやったことの単純化されたバージョンです:

def process(arglist...) = yada

val tasks = filenameGroups.map {
  fg =>
    val parser = new nu.xom.Builder()
    scala.actors.Futures.future {
      process(fg, parser)
    }
}

val pairs = tasks.par.flatMap(_.apply)
4

1 に答える 1

2

Futuresは、必要なスレッド数や計算に必要なメモリ量を実際に予測できないため、適切にシリアル化された計算を適度な数のFutures内に配置するのは一般的にユーザーの責任です。特に、8コアのマシンを使用している場合は、グループをそれほど小さくしたくないでしょうsomeStringListOfFilepaths.length/8(ファイルが大きすぎて、一度に8をメモリに入れることができない場合はそれより少なくなります)。考えずにマシンごとにスケーリングしたい場合は、SOや他の多くの場所でカバーされているコアの数を検査する標準のJavaトリックを使用できます。Runtime.getRuntime.maxMemory(コアが多く、RAMが少ない(またはVMに割り当てられていない)マシンを使用している場合に備えて、その場合も検査することをお勧めします。)

(ちなみに、あなたの最小限の例では、怠惰と先物の両方がありますが、怠惰はあなたのために何もしません。先物は作成されたときにすでに実行されていないので、先物のインスタンス化を遅らせることはおそらくあなたを助けません。)

また、200kのファイルがある場合、結果は200kになり、結果の大きさによっては、大量のメモリを消費する可能性があることに注意してください。おそらく32Gではありませんが、ファイルの内容を誰が知っていますか?

于 2012-10-16T19:37:39.697 に答える