k4200’s notes and thoughts

Programmer side of k4200

Parallel Collectionのベンチマークやってみた

xuwei_kさんが面白そうなこと(scala2.9のparallel collection の benchmark をしてみた)をやってたので、ちょっと自分でもやってみることにした。

準備

まずはScalaの最新版を取ってきてビルド。この辺を参考に。

さてscalaを起動

あとJVMの引数のXmx を 4096M にしてます

との事なので自分もそれに倣う。Xmsも指定すべきか分からなかったので、意味もなく1024にしてみた。4096にしとけばよかったかもしれないけどまあよしとする。

ちなみにメモリのサイズ指定の仕方はここを参考に。

>set "JAVA_OPTS=-Xmx4096M -Xms1024M"

>scala
Welcome to Scala version 2.10.0.r0-b20110322031631 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_21).
Type in expressions to have them evaluated.
Type :help for more information.

scala> scala.collection.parallel.availableProcessors
res0: Int = 8

さて実行!

scala> import collection.parallel
import collection.parallel

scala> def benchmark(times: Int)(f: =>Any) = new scala.testing.Benchmark{ def run = f }.runBenchmark(times)
benchmark: (times: Int)(f: => Any)List[Long]

scala> implicit def benchmark2report(list:List[Long]) = new { val average = (list.sum - list.max - list.min).toDouble / (list.size-2) }
benchmark2report: (list: List[Long])java.lang.Object{val average: Double}

scala> def test(listSize:Int){
     |    println("--------------" + listSize + "---------------")
     |    val normal = 1L to listSize toList //???ハ?フList?ツ?ュ??
     |    val parSeq = normal toParSeq       //?v?f?ェ?ッ?カparalell?ナ???ツ?ュ??
     |    val normalResult = benchmark(5)( normal.reduceLeft(_ + _) )
     |    //reduce?ニ?「?、?フ?ェ?Aparalell?ネcollection?ノ?オ?ゥ?ネ?「???\?b?h?ナ?A
     |    //???ナ?X???b?h?カ?ャ?オ?ェ???オ?ト?????オ?ト?????オ?「
     |    val parSeqResult = benchmark(5)( parSeq.reduce(_ + _) )
     |    println( normalResult , parSeqResult , parSeqResult.average / normalResult.average )
     | }
<console>:13: error: value toParSeq is not a member of List[Long]
          val parSeq = normal toParSeq       //?v?f?ェ?ッ?カparalell?ナ???ツ?ュ??

toParSeqていうのが無いというエラー。2.9と2.10で仕様が変わってるっぽい。代わりにParallelizableというtraitのparメソッドを使えばOKっぽい。

ちなみに、Windowsコマンドプロンプトにコピペしたので文字化けしているけど気にしない。

scala> def test(listSize:Int){
     |    println("--------------" + listSize + "---------------")
     |    val normal = 1L to listSize toList //???ハ?フList?ツ?ュ??
     |    val parSeq = normal par       //?v?f?ェ?ッ?カparalell?ナ???ツ?ュ??
     |    val normalResult = benchmark(5)( normal.reduceLeft(_ + _) )
     |    //reduce?ニ?「?、?フ?ェ?Aparalell?ネcollection?ノ?オ?ゥ?ネ?「???\?b?h?ナ?A
     |    //???ナ?X???b?h?カ?ャ?オ?ェ???オ?ト?????オ?ト?????オ?「
     |    val parSeqResult = benchmark(5)( parSeq.reduce(_ + _) )
     |    println( normalResult , parSeqResult , parSeqResult.average / normalResult.average )
     | }
test: (listSize: Int)Unit

これでOK。

ようやく実行

scala> 2000000 to 20000000 by 2000000 foreach test
--------------2000000---------------
(List(42, 31, 31, 31, 30),List(47, 16, 15, 14, 15),0.4946236559139785)
--------------4000000---------------
(List(279, 63, 61, 61, 59),List(27, 28, 28, 28, 28),0.4540540540540541)
--------------6000000---------------
(List(98, 90, 90, 92, 94),List(42, 42, 41, 44, 41),0.45289855072463764)
--------------8000000---------------
(List(2178, 131, 128, 129, 131),List(48, 49, 46, 52, 47),0.3682864450127877)
--------------10000000---------------
(List(149, 150, 148, 150, 147),List(83, 82, 79, 81, 95),0.5503355704697986)
--------------12000000---------------
(List(190, 176, 177, 181, 187),List(80, 79, 88, 89, 89),0.47155963302752296)
--------------14000000---------------
(List(219, 227, 220, 217, 213),List(106, 127, 109, 107, 105),0.49085365853658536)
--------------16000000---------------
(List(292, 244, 243, 244, 247),List(126, 124, 131, 124, 124),0.508843537414966)
--------------18000000---------------
(List(325, 267, 273, 279, 281),List(147, 143, 146, 154, 157),0.5366146458583433)
--------------20000000---------------
(List(340, 327, 298, 299, 302),List(138, 140, 140, 141, 146),0.45366379310344834)

結果

4コアの時と比べて劇的な変化は無いような。というか、畳み込み処理ってどうやって並列化するんだろう?その辺から勉強する必要がありそうだ。

ちなみに、実行時のCPU使用率はこんな感じ。