CLOVER🍀

That was when it all began.

ScalaのParallel Collectionsで遊ぶ

Scala 2.9.0の目玉といえば、Parallel Collectionsですよね。ちょっとこれで遊んでみたいと思います。

Scala 2.9.0 finalリリースページ
http://www.scala-lang.org/node/9483

上のページによると、「par」メソッドでこれまでのCollectionからParallel Collectionを作成し、Parallel Collectionから通常のCollectionに戻す時は「seq」メソッドを利用するのだとか。

早速試してみましょう。まずはREPLを起動して、Listを作成

$ scala
Welcome to Scala version 2.9.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_24).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val xs = List(1, 2, 3, 4, 5)
xs: List[Int] = List(1, 2, 3, 4, 5)

これをParallel Collectionに変換してみましょう。

scala> xs.par
res0: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(1, 2, 3, 4, 5)

おお、あっさり変わりました。が…Vectorになるの?確かにリリースノートには、ListのParallel版はないですよねぇ…。ScalaのListはリンクリストなので、直接並列処理には向かないということかなぁ?

とりあえず、foreachで出力してみます。

scala> xs.par.foreach(println)
1
3
4
5
2

なんか、出力順が変わってますね。一応並列に動いているってことでしょうか?

続いて、Parallel Collectionを普通のCollectionに戻してみましょう。

scala> xs.par.seq
res2: scala.collection.immutable.Seq[Int] = Vector(1, 2, 3, 4, 5)

Vectorになっとる…。Listから始まったはずなんですけど、完全に元に戻ってくるわけじゃないのねー。

これはちょっと変換規則が気になりますね。調べてみましょう。
不変CollectionからParallel Collectionへ変換

変換前 Parallel Collection変換後
List ParSeq(実体の型としてはParVector)
Vector ParVector
Stack ParSeq(実体の型としてはParVector)
Queue ParSeq(実体の型としてはParVector)
Range ParRange
Map ParMap(実体の型としてはParHashMap)
HashMap ParHashMap
TreeMap ParMap(実体の型としてはParHashMap)
ListMap ParMap(実体の型としてはParHashMap)
Set ParSet(実体の型としてはParHashSet)
HashSet ParHashSet
BitSet ParSet(実体の型としてはParHashSet)
ListSet ParSet(実体の型としてはParHashSet)

※StreamはParallel Collectionに変換しようとして評価し続けるので、OutOfMemoryになって失敗します

続いて可変CollectionからParallel Collectionへ

変換前 Parallel Collection変換後
Array ParArray
Buffer ParSeq(実体の型としてはParArray)
ArrayBuffer ParArray
ListBuffer ParSeq(実体の型としてはParArray)
StringBuilder ParSeq(実体の型としてはParArray)
LinkedList ParSeq(実体の型としてはParArray)
DoubleLinkedList ParSeq(実体の型としてはParArray)
Stack ParSeq(実体の型としてはParArray)
Queue ParSeq(実体の型としてはParArray)
Map ParMap(実体の型としてはParHashMap)
HashMap PerHashMap
OpenHashMap ParMap(実体の型としてはParHashMap)
LinkedHashMap ParMap(実体の型としてはParHashMap)
ListMap ParMap(実体の型としてはParHashMap)
Set ParSet(実体の型としてはParHashSet)
HashSet ParHashSet
LinkedHashSet ParSet(実体の型としてはParHashSet)

Collection全部を挙げたわけではありませんが、とりあえずこんなもんでしょうか?StringBuilderもParallel化できるんですねぇ。とりあえず、リリースノートで書かれているParallel Collectionの一覧を見ると、だいたい予想通りといえば予想通り。

んじゃ、逆変換はどうでしょう?
不変Parallel Collectionから不変Collectionへ変換

変換前 Collection変換後
ParVector Vector
ParRange Range
ParHashMap HashMap(ParMap型から変換した場合は、Map型で受け取る)
ParHashSet HashSet(ParSet型から変換した場合は、Set型で受け取る)

可変Parallel Collectionから可変Collectionへ変換

変換前 Collection変換後
ParArray ArraySeq
ParHashMap HashMap(ParMap型から変換した場合は、Map型で受け取る)
ParHashSet HashSet(ParSet型から変換した場合は、Set型で受け取る)

というわけで、通常のCollectionからParallel Collectionに変換した後に戻ってこようとすると、必ずしも同じ型には戻らないことがわかりました。元々のCollectionの型に戻そうと思ったら、seqメソッドを呼び出した後にもっかいCollectionを構成しなくちゃダメってことですね。

なお、通常のCollectionからいちいち作成しなくても、いきなりParallel Collectionを作成することも可能です。

scala> scala.collection.parallel.immutable.ParVector(1, 2, 3, 4, 5)
res0: scala.collection.parallel.immutable.ParVector[Int] = ParVector(1, 2, 3, 4, 5)