2010-02-03 93 views
4

我想在斯卡拉使用併發編程。基於this example這裏在 StackOverflow,我做了一個程序根據項目歐拉Problem 1。 我嘗試三種方法:第一種是簡單的執行,沒有平行性。其次 通過Executors和Callables使用java.util.concurrency API。第三,基於上面提到的頁面,使用scala.Futures。我的目標是比較執行時間。斯卡拉併發處理

這是代碼:

package sandbox 

import java.util.concurrent._ 
import scala.actors._ 

object TestPool { 

    def eval(n: Int): Boolean = (n % 3 == 0) || (n % 5 == 0) 

    def runSingle(max: Int): Int = (1 until max).filter(eval(_)).foldLeft(0)(_ + _) 

    def runPool(max: Int): Int = { 

    def getCallable(i: Int): Callable[Boolean] = new Callable[Boolean] { def call = eval(i) } 

    val pool = Executors.newFixedThreadPool(5) 
    val result = (1 until max).filter(i => pool.submit(getCallable(i)).get).foldLeft(0)(_ + _) 
    pool.shutdown 
    pool.awaitTermination(Math.MAX_LONG, TimeUnit.SECONDS) 

    result 
    } 

    def runFutures(max: Int): Int = (1 until max).filter(i => Futures.future(eval(i)).apply).foldLeft(0)(_ + _) 

    /** 
    * f is the function to be runned. it returns a Tuple2 containing the sum and the 
    * execution time. 
    */ 
    def test(max: Int, f: Int => Int): (Int, Long) = { 
    val t0 = System.currentTimeMillis 
    val result = f(max) 
    val deltaT = System.currentTimeMillis - t0 

    (result, deltaT) 
    } 


    def main(args : Array[String]) : Unit = { 
    val max = 10000 

    println("Single : " + test(max, runSingle)) 
    println("Pool : " + test(max, runPool)) 
    println("Futures: " + test(max, runFutures)) 
    } 
} 

這些結果如下:

最大= 10:

  • 單:(23,31)
  • 游泳池:(23, 16)
  • 期貨:(23,31)

最大= 100:

  • 單:(2318,33)
  • 游泳池:(2318,31)
  • 期貨:(2318,55)

最大= 1000:

  • 單:(233168,42)
  • 池:(233168 ,111)
  • 期貨:(233168,364)

最大= 10000:

  • 單:(23331668,144)
  • 游泳池:(23331668,544)
  • 期貨:...我在3分鐘後取消執行

很明顯,我無法正確使用來自Java和Scala的併發API。所以我問: 我的錯誤在哪裏?什麼是使用併發的更合適的形式? 關於Scala演員?有可能使用它們嗎?

回答

1

你期待什麼結果?你是否期望這些方法中的一種比其他方法更好?你是否期望程序針對不同的執行方法進行不同的縮放?

你的機器有多少核心?如果你只有一個核心,那麼你應該預計時間會隨着工作線性增加。在運行過程中,你的CPU使用情況如何?數字是否可重複?

您還沒有考慮到JVM Hotspot預熱時間的影響,這可能會導致像這樣的微基準測試出現嚴重問題。

1

我假設你使用的是Scala 2.7。基本上,filtermapRange1 until max的結果)是非嚴格的,這意味着它將按需計算,並且每次嘗試訪問它的結果時都會計算它。

試試這個,例如:

val y = (1 to 10).filter{x => println("Filtering "+x); x % 2 == 0}.map{x => println("Mapping "+x); x * 2} 
println(y(0)) 
println(y(1)) 
println(y(2)) 
println(y(0)) 

結果,反正是你的東西並行串行運行。添加一個.force範圍,它會沒事的。