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

CanBuildFromそれが起こる理由の説明は長い話です: Scala 2.9.x (他のバージョンについては知りません) では、filter や map などのコレクション メソッドはメカニズムに依存しています。アイデアは、新しいコレクションのビルダーを作成するために使用される暗黙的なパラメーターがあるということです。

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 を使用して 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 並列コレクションは、map メソッドの並列ビルダーを作成する特別な 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コンパイラが並列コレクションに を提供しない場合、マップは並列になりません。

于 2013-01-18T16:22:36.233 に答える