3

我有一堆我试图并行处理的 xml 文件。我使用 future 的 scala 代码(2.9.2)开始时很好,但最终吃掉了我机器上近 100% 的 32G。当我按顺序执行此操作时不会发生这种情况,所以我的猜测是在使用 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 个)。如果您想在每台机器上扩展它而无需考虑它,您可以使用检查核心数量的标准 Java 技巧,涵盖在 SO和许多其他地方。(在这种情况下也可能需要检查Runtime.getRuntime.maxMemory,以防万一您使用的机器有很多内核但内存不多(或为 VM 分配的内存不多)。)

(顺便说一句,在您的最小示例中,有惰性和期货,但惰性对您没有任何帮助。期货在创建时已经没有运行,因此延迟期货的实例化可能对您没有任何帮助。)

另外,请注意,如果您有 200k 个文件,最终将得到 200k 个结果,并且根据结果的大小,这可能会占用大量内存。可能不是 32G,但谁知道文件中有什么?

于 2012-10-16T19:37:39.697 回答