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)