我尝试将其作为可选项顺序或并行运行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
我注意到'map'操作并不像我预期的那样并行运行。虽然没有条件,但它会:
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会发生这种情况。
答案 0 :(得分:3)
对其发生原因的解释是一个很长的故事:在Scala 2.9.x中(我不知道其他版本)这些集合方法(如filter或map)依赖于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方法仅在TraversableLike
特征中定义,其子类不需要覆盖它。如您所见,在方法映射签名中有许多类型参数。让我们来看看琐碎的事情:
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。
在合理的情况下,地图方法已在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(并行集合)为地图方法创建并行构建器。
然而,当你做
时会发生什么(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
,则映射将不是并行的。