请注意:这不是一个重复的问题,因为此问题指定了Iterator
所有方法,而不只是map
和flatMap
。因此Future.traverse
不是一个好的答案。
假设我有这个简单的陈述:
(1 to 100).toSet.subsets.find(f)
完美无缺。它是懒惰的,不会使用大量内存,只要找到一个元素就会返回。当您想并行化时,问题就开始了。你可能会说,它是Scala,必须有.par
或Iterator
,但没有。{/ p>
互联网上提出的解决方案是使用.grouped
,但它并不像我想要的那么好。为什么呢?
val it = (1 to 100).toSet.subsets.grouped(1000000).map(_.par.find(f)).flatten
if (it.hasNext) Some(it.next) else None
使用更多内存。我知道它仍然是O(1),但让我们在这里完美:)
它不是完全可并行化的(根据Amdahl定律)。当.grouped
正在使用下一个百万元素块的迭代器时,除了一个线程之外的所有元素都在等待。如果迭代器消耗昂贵,则这尤其成问题。此外,还需要产生一组新线程来处理新块的开销。
生成更复杂/更长的代码(参见示例)。如果Iterator
有.nextOption
,它会稍微缩短代码,但仍会。
除了编程我自己的生产者 - 消费者模型(迭代器是生产者,线程是消费者)然后最终减少步骤,还有什么其他的吗?
答案 0 :(得分:1)
您可以使用.toStream
。这将生成一个将记忆值的惰性流。它上面有.par
。
它将在堆上分配一些包装器但是如果你小心(不要保持指向流的指针),这只会导致GC压力,但不会增加剩余内存占用。它仍然会很快。请注意,并行集合会产生相当多的开销,如果你的元素计算不够昂贵,可能不值得。
迭代器太低而无法并行化。但实际上并不需要并行迭代器,而是迭代器的并行遍历,您可以从标准库中获得Future.traverse。