2012-10-16 190 views
3

我有一堆xml文件,我试图并行处理。我的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) 
+0

要同时处理多少个文件?看起来你至少有成千上万。将数千个XML文件加载到内存中将很快耗尽您的整个RAM。按顺序执行此操作基本上读取文件,进行处理,并有资格进行垃圾回收。 –

+0

@TomaszNurkiewicz我想处理尽可能多的,我有200K文件。我只是假设scala期货会很聪明,并且只能创建8个(或者你有多个处理器)未来的实例,所以我一次只能在内存中创建8个xml文档。 – JasonMond

回答

2

期货真的不能预知你想要多少线程或您的计算需要多少内存取,所以它一般你的责任,把适当的序列化计算内部数量不多的期货。特别是,如果你使用的是8核心机器,那么你可能不希望编组的数量远远小于someStringListOfFilepaths.length/8(如果你的文件太大以至于一次不能有8个内存,那么这个数量会减少)。如果您想在每台计算机上扩展而不必考虑核心数量,则可以使用标准Java技巧来检查核心数量,covered on SO和许多其他地方。 (在这种情况下,可能也要检查Runtime.getRuntime.maxMemory,以防万一你的计算机内核数量多,内存不足(或者虚拟机分配不多)。)

(顺便说一句,在你最小的例子中有懒惰和期货,但懒惰对你没有任何作用,创建时期货已经没有运行,所以延迟期货的实例化可能对你没有任何帮助。)

另外,请注意,如果你有20万个文件,你最终会得到200K个结果,并且取决于结果有多大,这可能会消耗大量的内存。可能不是32G,但谁知道文件中有什么?

+0

我已经按顺序运行了这段代码,它从来没有击中超过5%的内存使用。另外,根据你的建议,我尝试将团队规模提高到30K。它仍然最终达到几乎100%的内存使用。 – JasonMond

+0

@JasonMond - 如果你在一个组中运行所有文件会怎么样?也就是说,如果它是连续的而不是仅仅包裹在未来呢?我不知道平行的东西和期货是否是一个红鲱鱼,它实际上是关于你的fg.map与分组流? –

+1

是不是流是记忆和他保持参考头(任务) –