

並列で実行すると、1つの処理が重くなってしまいます。

[code]

    $ scala
    Welcome to Scala version 2.11.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_25).
    Type in expressions to have them evaluated.
    Type :help for more information.
    
    // 処理時間を計測する関数 
    scala> def elapsed(f: => Unit) = { val s = new java.util.Date; f; println("time: " + (new java.util.Date().getTime - s.getTime) + "ms") }
    elapsed: (f: => Unit)Unit
    
    // 何か重い処理
    scala> def f = (1 to 1000000).map(_ + scala.util.Random.nextInt(10000))
    f: scala.collection.immutable.IndexedSeq[Int]
    
    // 普通に30回重い処理を実行
    scala> (1 to 30).foreach { _ => elapsed(f) }
    time: 166ms
    time: 84ms
    time: 36ms
    time: 35ms
    time: 64ms
    time: 55ms
    time: 52ms
    time: 55ms
    time: 54ms
    time: 54ms
    time: 58ms
    time: 54ms
    time: 52ms
    time: 53ms
    time: 51ms
    time: 56ms
    time: 57ms
    time: 52ms
    time: 63ms
    time: 56ms
    time: 54ms
    time: 70ms
    time: 72ms
    time: 55ms
    time: 119ms
    time: 106ms
    time: 99ms
    time: 86ms
    time: 93ms
    time: 61ms
    
    // 今度はparで並列に実行
    scala> (1 to 30).par.foreach { _ => elapsed(f) }
    time: 201ms
    time: 212ms
    time: 215ms
    time: 221ms
    time: 176ms
    time: 217ms
    time: 226ms
    time: 251ms
    time: 242ms
    time: 230ms
    time: 235ms
    time: 268ms
    time: 238ms
    time: 235ms
    time: 235ms
    time: 383ms
    time: 372ms
    time: 382ms
    time: 396ms
    time: 414ms
    time: 407ms
    time: 337ms
    time: 324ms
    time: 354ms
    time: 390ms
    time: 407ms
    time: 452ms
    time: 159ms
    time: 147ms
    time: 37ms
    
[/code]

parでの例ですが、Futureでやっても同じような現象でした。

コア数は4つなので並列度は4ですが、さらに並列度を上げると比例してしてさらに重くなります。

処理時間が10倍〜とかなるので、10秒だと100秒とかになって...。

"PCって色々アプリ動かすと急に重くなるよね"みたいな当たり前の現象なのかな...と思ったりもするんですが、よく分かってません...。

何か回避策みたいなものがあるんでしょうか。

詳しい方ご教授ください。よろしくおねがいします。


CC BY-SA 3.0
2015-07-08T14:44:50.190
12165
2015-07-09T03:10:36.010
9460
question
scala
並列処理すると1つの処理が重くなる


純粋に計算負荷をかけるタスクにすると、想定された動きをします。

[code]

    // weightの値は処理性能に合わせて調整してください。30前後が良さそう。
    def f(weight: Int): Unit = {
      def fib(n: Int): Int = n match {
        case 0 => 1
        case 1 => 1
        case _ => fib(n - 2) + fib(n - 1)
      }
      fib(weight)
    }
    
    // ついでにThread IDも出す表示関数にします
    def elapsed(f: => Unit) = {
        val s = new java.util.Date;
        f;
        println("thread: " + Thread.currentThread().getId() + "\ttime:\t" + (new java.util.Date().getTime - s.getTime) + "ms")
    }
    
[/code]

[code]

    // 外側のelapsedが最後に合計を表示
    scala> elapsed((1 to 30).foreach { _ => elapsed(f(30)) })
    thread: 1       time:   5ms
    thread: 1       time:   4ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   4ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   6ms
    thread: 1       time:   6ms
    thread: 1       time:   7ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   4ms
    thread: 1       time:   5ms
    thread: 1       time:   5ms
    thread: 1       time:   152ms
    
    scala> elapsed((1 to 30).par.foreach { _ => elapsed(f(30)) })
    thread: 56      time:   5ms
    thread: 58      time:   5ms
    thread: 49      time:   5ms
    thread: 56      time:   5ms
    thread: 58      time:   4ms
    thread: 49      time:   5ms
    thread: 54      time:   13ms
    thread: 56      time:   5ms
    thread: 58      time:   5ms
    thread: 49      time:   4ms
    thread: 54      time:   5ms
    thread: 56      time:   4ms
    thread: 58      time:   5ms
    thread: 49      time:   5ms
    thread: 54      time:   4ms
    thread: 56      time:   5ms
    thread: 49      time:   5ms
    thread: 58      time:   5ms
    thread: 55      time:   5ms
    thread: 56      time:   5ms
    thread: 49      time:   5ms
    thread: 58      time:   5ms
    thread: 55      time:   5ms
    thread: 56      time:   5ms
    thread: 55      time:   5ms
    thread: 57      time:   4ms
    thread: 56      time:   4ms
    thread: 59      time:   4ms
    thread: 58      time:   9ms
    thread: 49      time:   9ms
    thread: 1       time:   42ms
    
[/code]

(出力例のスレッドIDが大きいのは、何度か走らせたらIDが大きくなっただけなので、気にしないでください。並列度8)

元々遅くなっていたのは、scala.util.Randomの挙動に起因する問題に思えます。もし実際のアプリケーションでRandomを使う必要があるなら、実装を追って原因追究しないといけません。  
そうではなくて並列化の挙動を見たいだけなら、挙動を良く知らないものは使わないほうが無難でしょう。


CC BY-SA 3.0
2015-07-09T02:02:08.913
12178
2015-07-09T02:02:08.913
799
12165
answer


  * scala.util.Randomは[java.util.Random](http://docs.oracle.com/javase/jp/7/api/java/util/Random.html)を使うので複数スレッドから同時にアクセスする用途には向かない。
  * `(1 to 1000000).map(..)`で大量にメモリが確保される。

という理由で性能出ていないようです。[ThreadLocalRandom](http://docs.oracle.com/javase/jp/7/api/java/util/concurrent/ThreadLocalRandom.html)とforeachに書き換えるとオーバーヘッドはありますが、質問の例よりは改善されます。

[code]

    scala> def f5 = (1 to 1000000).foreach(_ => java.util.concurrent.ThreadLocalRandom.current().nextInt(10000))
    
    f5: Unit
    
    scala> (1 to 30).foreach { _ => elapsed(f5) }
    time: 21ms
    time: 31ms
    time: 19ms
    time: 22ms
    time: 17ms
    time: 17ms
    time: 20ms
    time: 19ms
    time: 24ms
    time: 21ms
    time: 17ms
    time: 21ms
    time: 17ms
    time: 20ms
    time: 17ms
    time: 17ms
    time: 29ms
    time: 25ms
    time: 25ms
    time: 17ms
    time: 17ms
    time: 30ms
    time: 36ms
    time: 19ms
    time: 17ms
    time: 18ms
    time: 19ms
    time: 17ms
    time: 20ms
    time: 16ms
    
    scala> (1 to 30).par.foreach { _ => elapsed(f5) }
    time: 53ms
    time: 53ms
    time: 54ms
    time: 56ms
    time: 62ms
    time: 63ms
    time: 69ms
    time: 74ms
    time: 51ms
    time: 43ms
    time: 51ms
    time: 50ms
    time: 59ms
    time: 43ms
    time: 55ms
    time: 50ms
    time: 37ms
    time: 40ms
    time: 43ms
    time: 42ms
    time: 37ms
    time: 46ms
    time: 41ms
    time: 35ms
    time: 32ms
    time: 34ms
    time: 37ms
    time: 32ms
    time: 30ms
    time: 30ms
    
[/code]


CC BY-SA 3.0
2015-07-09T03:10:36.010
12183
2015-07-09T03:10:36.010
241
12165
answer