处理scala.collections.parallel中令人惊讶的ParList缺失

时间:2011-07-10 16:16:40

标签: list scala map parallel-processing scala-collections

因此scala 2.9最近出现在Debian测试中,带来了新奇的并行集合。

假设我有一些等同于

的代码
  def expensiveFunction(x:Int):Int = {...}

  def process(s:List[Int]):List[Int} = s.map(expensiveFunction)

现在,在我的机器上实际显示文档之前,我已经收集了关于并行集合的那些小小的内容,我期待通过将List切换到ParList来并行化这个...但令我惊讶的是,没有一个! (只需ParVectorParMapParSet ...)。

作为一个工作场所,这个(或一行等效的)似乎运作良好:

  def process(s:List[Int]):List[Int} = {
    val ps=scala.collection.parallel.immutable.ParVector()++s
    val pr=ps.map(expensiveFunction)
    List()++pr
  }

在我的测试代码中产生了大约x3的性能提升,并实现了更高的CPU使用率(四核加上超线程i7)。但它看起来很笨重。

我的问题是一种汇总的:

  • 为什么没有ParList
  • 鉴于没有ParList,是否有 我应该采用更好的模式/成语 我觉得他们不想失踪?
  • 使用列表a,我只是“落后于时代” 在我的scala程序中很多(就像我所有的Scala书籍一样) 在2.7天内买回来教我)和 我应该更多地使用它 Vectors? (我的意思是在C ++领域 我通常需要一个很好的理由来使用 std::list std::vector以上{{1}}。

3 个答案:

答案 0 :(得分:14)

当你想要模式匹配(即List)和有效的前置/迭代时,

case x :: xs很棒。但是,当您想要快速索引,分割成块或加入(即xs ::: ys)时,它们并不是那么好。

因此,当你认为这种事情(分裂和连接)完全高效并行所需要的时候,它没有多大意义(有一个并行List)。使用:

xs.toIndexedSeq.par

答案 1 :(得分:8)

首先,让我向您展示如何制作该代码的并行版本:

def expensiveFunction(x:Int):Int = {...}

def process(s:List[Int]):Seq[Int] = s.par.map(expensiveFunction).seq

这将让Scala为您解决问题 - 顺便说一下,它使用ParVector。如果您真的想要List,请拨打.toList而不是.seq

至于问题:

  • 没有ParList,因为List是一个本质上非并行的数据结构,因为对它的任何操作都需要遍历。

  • 例如,您应该编写特征代码而不是代码 - SeqParSeqGenSeqList保证了LinearSeq的性能特征。

  • Scala 2.8之前的所有书籍都没有考虑新的馆藏库。特别是,这些集合确实没有共享一致且完整的API。现在他们这样做了,你可以通过利用它获得更多收益。

    此外,Scala 2.7中没有像Vector这样的集合 - 一个具有(接近)常量索引访问权限的不可变集合。

答案 2 :(得分:7)

List不能轻易地拆分成各种子列表,这使得很难并行化。首先,它有O(n)访问权限;同样List也不能剥离它的尾部,因此需要包含一个长度参数。

我想,采用Vector将是更好的解决方案。

请注意,Scala的Vectorstd::vector不同。后者基本上是标准数组的包装器,内存中的连续块需要在添加或删除数据时不时被复制。 Scala的Vector是一种专门的数据结构,它允许有效的复制和拆分,同时保持数据本身不可变。