经由到一个祖先类型的引用调用上的并行集合地图(Calling map on a parallel c

2019-07-18 10:01发布

我试图使它可选运行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)我希望是这两种类型的最接近共同祖先theListtheList.par的是,我不会在这里贴上一个可怕的类型,但看起来也同样吸引在(通过控制台阶:)

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

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

更新:这是在讨论SI-4843 ,但是从JIRA票,目前还不清楚为什么这对斯卡拉2.9.x.发生

Answer 1:

为什么它发生的解释是一个很长的故事:在斯卡拉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是新类型的集合,这可以从目前的类型不同。 一个经典的例子是,当你使用一个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平行否则倒在默认的实现。 因此,斯卡拉并行收集使用特殊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集合的实现中,如果行为会有所不同,这将意味着非并行收集的每一个地图将并行运行也是如此。

这里的关键是,对于Scala集合是如何设计在2.9.x别无他法。 如果编译器不提供CanBuildFrom的并行采集,地图将不平行。



文章来源: Calling map on a parallel collection via a reference to an ancestor type