1

我尝试使其可选择map顺序或并行运行操作,例如使用以下代码:

val runParallel = true
val theList = List(1,2,3,4,5)
(if(runParallel) theList.par else theList) map println //Doesn't run in parallel

我注意到的是,“地图”操作并没有像我预期的那样并行运行。虽然没有条件,但它会:

theList.par map println   //Runs in parallel as visible in the output

(if(runParallel) theList else theList.par)我希望是这两种类型的最接近的共同祖先的表达式的类型,theList并且theList.par是一种可怕的类型,我不会在这里粘贴,但看起来很有趣(通过 scala 控制台:)

:type (if(true) theList else theList.par)

为什么map并行集合不并行工作?

更新:这在SI-4843中进行了讨论,但从 JIRA 票证中不清楚为什么会在 Scala 2.9.x 上发生这种情况。

4

1 回答 1

3

为什么会发生这种情况的解释说来话长:在 Scala 2.9.x(我不知道其他版本)中,过滤器或映射等集合方法依赖于CanBuildFrom机制。这个想法是您有一个隐式参数,用于为新集合创建构建器:

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
    val b = bf(repr)
    b.sizeHint(this) 
    for (x <- this) b += f(x)
    b.result
  }

由于这种机制,map 方法仅在TraversableLiketrait 中定义,其子类不需要覆盖它。如您所见,在方法映射签名内部有许多类型参数。让我们看看琐碎的:

  • B是新集合的元素的类型
  • A是源集合中元素的类型

让我们看看更复杂的:

  • That是新的集合类型,可以与当前类型不同。一个经典的例子是当你使用 toString 映射 BitSet 时:

     scala> val a = BitSet(1,3,5)
     a scala.collection.immutable.BitSet = BitSet(1, 3, 5)
     scala>  a.map {_.toString}
     res2: scala.collection.immutable.Set[java.lang.String] = Set(1, 3, 5)
    

由于创建地图是非法的,BitSet[String]因此您的地图结果将是Set[String] 最终Repr是当前集合的类型。当您尝试将集合映射到函数时,编译器将使用类型参数解析合适的 CanBuildFrom。

由于它是合理的,map 方法已在并行集合中被覆盖,ParIterableLike如下所示:

 def map[S, That](f: T => S)(implicit bf: CanBuildFrom[Repr, S, That]): That = bf ifParallel { pbf =>
    executeAndWaitResult(new Map[S, That](f, pbf, splitter) mapResult { _.result })
  } otherwise seq.map(f)(bf2seq(bf))

如您所见,该方法具有相同的签名,但它使用了不同的方法:它测试提供的是否 CanBuildFrom是并行的,否则将退回到默认实现。因此,Scala 并行集合使用特殊的 CanBuildFrom(并行集合),它为 map 方法创建并行构建器。

但是,当你这样做时会发生什么

(if(runParallel) theList.par else theList) map println //Doesn't run in parallel

map 方法是在结果上执行的

  (if(runParallel) theList.par else theList) 

其返回类型是两个类的第一个共同祖先(在这种情况下,只是一定数量的特征混合在一起)。既然是共同祖先,就是类型参数Repr会是两种集合的某种共同祖先的表示,姑且称之为Repr1


结论

当您调用该map方法时,编译器应该找到一个适合CanBuildFrom[Repr, B, That]该操作的方法。由于我们Repr1不是并行集合中的一个,因此不会有任何CanBuildFrom[Repr1,B,That]能够提供并行构建器的能力。这实际上是关于 Scala 集合实现的正确行为,如果行为不同,则意味着非并行集合的每个映射也将并行运行。

这里的重点是,对于 Scala 集合在 2.9.x 中的设计方式,没有其他选择。如果编译器没有CanBuildFrom为并行集合提供 a,则映射将不是并行的。

于 2013-01-18T16:22:36.233 回答