我正在使用Scala处理大数据,因此内存和时间是比我通常更重要的伴侣。我试图通过在大型源文件上细分Iterator[String]
获得的初始getLines
来提高某些评估的速度,以便并行进行一些子评估并合并结果。我通过递归slice
将迭代器分成两半,并在每个子实例上调用递归函数。
现在,我想知道为什么我得到GCoverhead或JavaHeapSpace异常,虽然“关键”元素只在递归步骤之前评估一次(为了获得迭代器的大小),但在我看来不是在递归步骤中,因为slice
再次返回一个迭代器(实现时非严格)。在连接子列表之前,以下(简化!)代码将无法应用于〜15g文件。
我在每一步都使用.duplicate
。我查看了api,.duplicate
的文档说“实现可以为一个迭代器迭代的元素分配临时存储,但另一个迭代器没有。”但是还没有迭代任何元素。有人能给我一个暗示,那里出了什么问题以及如何解决这个问题?非常感谢你!
type itType = Iterator[String]
def src = io.Source.fromFile(args(0)).getLines
// recursively divide into equal size blocks in divide&conquer fashion
def getSubItsDC(it: itType, depth: Int = 4) = {
println("Getting length of file..")
val totalSize = src.length
println(totalSize)
def rec(it_rec: itType = it, depth_rec: Int = depth, size: Int = totalSize):
List[itType] = depth_rec match {
case n if n > 0 =>
println(n)
val (it1, it2) = it_rec.duplicate
val newSize = size/2
rec(it1 slice (0,newSize), n-1, newSize) ++
rec(it2 slice (newSize,size), n-1, newSize)
case n if n == 0 => List(it_rec)
}
println("Starting recursion..")
rec()
}
getSubItsDC(src)
在REPL中,代码运行速度与任意大小的迭代器一样快(当对totalSize进行硬编码时),因此我认为是正确的懒惰。
答案 0 :(得分:2)
我认为您最好使用itr grouped size
获取Iterator[Iterator[String]]
(GroupedIterator
):
scala> val itr = (1 to 100000000).iterator grouped 1000000
itr: Iterator[Int]#GroupedIterator[Int] = non-empty iterator
这将允许您对文件的某些部分进行处理。
复制Iterator
显然是一个操作,这意味着迭代器可能必须缓存其计算值。例如:
scala> val itr = (1 to 100000000).iterator
itr: Iterator[Int] = non-empty iterator
scala> itr filter (_ % 10000000 == 0) foreach println
10000000
....
100000000
但是当我拿一份副本时:
scala> val (a, b) = (1 to 100000000).iterator.duplicate
a: Iterator[Int] = non-empty iterator
b: Iterator[Int] = non-empty iterator
scala> a filter (_ % 10000000 == 0) foreach println
//oh dear, garbage collecting
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
在此示例中,当我浏览a
时,为b
重复,a
已迭代但b
的元素没有,需要缓存