Scala 大于内存大小的数据的并行采集处理

Scala 大于内存大小的数据的并行采集处理,scala,parallel-processing,parallel-collections,Scala,Parallel Processing,Parallel Collections,有没有一种简单的方法可以在不将完整集合加载到内存的情况下使用scala并行集合 例如,我有一个很大的集合,我只想在一个小的块上并行执行一个特定的操作(折叠),它适合内存,而不是在另一个块上,等等,最后重新组合所有块的结果 我知道,演员是可以使用的,但使用par集合真的很好 我已经写了一个解决方案,但它并不好: def split[A](list: Iterable[A], chunkSize: Int): Iterable[Iterable[A]] = { new Iterator[

有没有一种简单的方法可以在不将完整集合加载到内存的情况下使用scala并行集合

例如,我有一个很大的集合,我只想在一个小的块上并行执行一个特定的操作(折叠),它适合内存,而不是在另一个块上,等等,最后重新组合所有块的结果

我知道,演员是可以使用的,但使用par集合真的很好

我已经写了一个解决方案,但它并不好:

  def split[A](list: Iterable[A], chunkSize: Int): Iterable[Iterable[A]] = {
    new Iterator[Iterable[A]] {
      var rest = list
      def hasNext = !rest.isEmpty
      def next = {
        val chunk = rest.take(chunkSize)
        rest = rest.drop(chunkSize)
        chunk
      }
    }.toIterable
  }                                               

  def foldPar[A](acc: A)(list: Iterable[A], chunkSize: Int, combine: ((A, A) => A)): A = {
    val chunks: Iterable[Iterable[A]] = split(list, chunkSize)
    def combineChunk: ((A,Iterable[A]) => A) = { case (res, entries) => entries.par.fold(res)(combine) }
    chunks.foldLeft(acc)(combineChunk)
  }                                               

  val chunkSize = 10000000                        
    val x = 1 to chunkSize*10                 

    def sum: ((Int,Int) => Int) = {case (acc,n) => acc + n }

    foldPar(0)(x,chunkSize,sum)

你的想法非常简洁,可惜现在还没有这样的功能(AFAIK)

我只是把你的想法改写成一段简短的代码。首先,我觉得对于平行折叠来说,使用这个概念是很有用的——它是一个具有关联运算和零元素的结构。结合性很重要,因为我们不知道合并并行计算结果的顺序。零元素很重要,所以我们可以把计算分成块,然后开始从零开始折叠。不过,它并没有什么新东西,这正是Scala的集合所期望的

// The function defined by Monoid's apply must be associative
// and zero its identity element.
trait Monoid[A]
  extends Function2[A,A,A]
{
  val zero: A
}
接下来,Scala的
迭代器
s已经有了一个有用的方法
grouped(Int):groupeditor[Seq[a]]
,它将迭代器分割成固定大小的序列。它非常类似于您的
拆分
。这使我们能够将输入切割成固定大小的块,然后对其应用Scala的并行收集方法:

def parFold[A](c: Iterator[A], blockSize: Int)(implicit monoid: Monoid[A]): A =
  c.grouped(blockSize).map(_.par.fold(monoid.zero)(monoid))
                      .fold(monoid.zero)(monoid);
我们使用并行集合框架折叠每个块,然后(没有任何并行化)合并中间结果

例如:

// Example:
object SumMonoid extends Monoid[Long] {
  override val zero: Long = 0;
  override def apply(x: Long, y: Long) = x + y;
}
val it = Iterator.range(1, 10000001).map(_.toLong)
println(parFold(it, 100000)(SumMonoid));

我想说,这里正确的计算模型是map reduce(因此可能是),而不是actors本身。形式上——是的,但在这种情况下处理时间是不合理的,所以在一台机器上运行完全可以。monoid的使用很好,以前从未知道过。关于分组方法,我怀疑它是否可以将所有内容加载到内存中,但事实证明它不能。稍后我将测试您的解决方案,但它似乎应该可以工作,而且更简洁。非常感谢@MikhailGolubtsov请告诉我你的测试进展如何,我也很好奇。我自己只做了一些非常基本的测试,所以我运行了一个大集合的处理,并没有超出堆空间。所以它确实有效。但在我的任务中,我注意到只有一些并行性,迭代器中的条目在单个线程中进行预处理。所以它可以进一步改进。@MikhailGolubtsov是的,我知道。首先,在每个内部平行褶皱的末端,如果一个块的一部分比其他部分需要更长的时间来计算,则可能会出现没有充分利用岩芯的情况。其次,如果在主迭代器上调用
next
需要一些可测量的时间,那么这将不会被并行化。