并行化Scala的迭代器

时间:2014-05-20 09:35:45

标签: scala parallel-processing iterator scala-collections

请注意:这不是一个重复的问题,因为此问题指定了Iterator所有方法,而不只是mapflatMap。因此Future.traverse不是一个好的答案。

假设我有这个简单的陈述:

(1 to 100).toSet.subsets.find(f)

完美无缺。它是懒惰的,不会使用大量内存,只要找到一个元素就会返回。当您想并行化时,问题就开始了。你可能会说,它是Scala,必须有.parIterator,但没有。{/ p>

互联网上提出的解决方案是使用.grouped,但它并不像我想要的那么好。为什么呢?

val it = (1 to 100).toSet.subsets.grouped(1000000).map(_.par.find(f)).flatten
if (it.hasNext) Some(it.next) else None
  1. 使用更多内存。我知道它仍然是O(1),但让我们在这里完美:)

  2. 它不是完全可并行化的(根据Amdahl定律)。当.grouped正在使用下一个百万元素块的迭代器时,除了一个线程之外的所有元素都在等待。如果迭代器消耗昂贵,则这尤其成问题。此外,还需要产生一组新线程来处理新块的开销。

  3. 生成更复杂/更长的代码(参见示例)。如果Iterator.nextOption,它会稍微缩短代码,但仍会。

  4. 除了编程我自己的生产者 - 消费者模型(迭代器是生产者,线程是消费者)然后最终减少步骤,还有什么其他的吗?

1 个答案:

答案 0 :(得分:1)

您可以使用.toStream。这将生成一个将记忆值的惰性流。它上面有.par

它将在堆上分配一些包装器但是如果你小心(不要保持指向流的指针),这只会导致GC压力,但不会增加剩余内存占用。它仍然会很快。请注意,并行集合会产生相当多的开销,如果你的元素计算不够昂贵,可能不值得。

迭代器太低而无法并行化。但实际上并不需要并行迭代器,而是迭代器的并行遍历,您可以从标准库中获得Future.traverse