2013-01-18 53 views
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 

的类型,我希望是两种类型的theListtheList.par的最接近共同祖先表达(if(runParallel) theList else theList.par)的是一个可怕的类型,我不会粘贴在这里,但它有趣的看(通过斯卡拉控制台:)

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

为什么没有map上并联并行采集工作?

更新:这在SI-4843中讨论过,但从JIRA票据中不清楚为什么这发生在Scala 2.9.x上。

回答

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 
    } 

由于这种机制,地图方法在TraversableLike特质只定义和它的子类不需要重写它。如您所见,在方法映射签名中有许多类型参数。让我们来看看在琐碎的:

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

让我们来看看在更复杂的问题:

  • That是新类型的集合,可以是从真正水流不同nt类型。一个经典的例子是,当你使用一个toString映射例如位集合:

    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 

发生什么地图方法大干快上的

(if(runParallel) theList.par else theList) 

返回类型为两个类的第一个共同祖先的结果执行(在这种情况下,只有一定数量的特性混合在一起)。既然它是一个共同的祖先,那么类型参数Repr将会是某种共同祖先的两种集合表示,我们称之为Repr1


结论

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

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

+0

感谢您的详细解答。 – omid