2012-02-09 50 views
3

因此,對於作業任務,我應該使用幾個線程機制,使用一個應該導致pi的函數的簡單集成。該實施應該處理超過500億美元的時間間隔。我目前的實現處理2GB堆大小約5000萬的for循環。現在我的問題是爲什麼實現使用這麼多的內存? (我認爲這是因爲範圍必須提前制定,這是真的嗎?)我該如何改善記憶體的使用?是否有可能使用並行集合,或者我是否被迫使用線程池來處理這樣的事情?斯卡拉並行用盡RAM

注意:以下實現將得到充分的評價。這是爲了我的智力好奇心和我在Scala中變得更流利的夢想。

import scala.Math 

object Pi { 
def calculate_pi(interval: Int): Double = { 
    val start: Long = System.currentTimeMillis; 
    var duration: Long = 0 
    var x: Double = 2.0/interval 
    var y: Double = 0.0 
    var mypi: Double = 0.0 

    (x until 1.0 by 1.0/interval).par.foreach(i => { 
     y = 4.0/(1.0+x*x) 
     mypi += (1.0/interval)*y 
    }) 

    duration = (System.currentTimeMillis - start) 
    println("scala calculate_pi\t" + interval + "\t" + duration + "ms\t" + mypi) 
    return mypi 
} 




object Main extends App { 
    println("Please input the interval for the calculation") 
    if(args.length < 1) { sys.exit } 
    val interval = args(0).toInt 
    Pi.calculate_pi_seq(interval) 
    Pi.calculate_pi(interval) 
} 
+5

它甚至工作嗎?看起來你正在同時修改mypi。而且,更平凡但更重要的是,你不使用你的循環變量我(你正在使用你的常量x) – 2012-02-09 08:23:14

+0

你可以發佈你得到的OutOfMemoryError嗎? – axel22 2012-02-09 09:20:50

+0

java.lang.OutOfMemoryError:Java堆空間 – Bbatha 2012-02-10 00:15:24

回答

6

這是各種錯誤的:

(x until 1.0 by 1.0/interval).par.foreach(i => { 
    y = 4.0/(1.0+x*x) 
    mypi += (1.0/interval)*y 
}) 

的第一個問題是,y所有的計算是一致的:不使用i在計算過程中。由於x不會更改,所有線程計算相同的值。

這裏是第二個問題,您可以並行計算mypi(和y。這意味着多個線程正在同時讀取和寫入mypiy

讓我們考慮一個執行來理解這個問題。假設第一個線程開始運行,計算y,然後讀取ymypi。該線程然後暫停,並且所有其他線程運行。最後,該線程繼續並將其計算結果寫入mypi。在這種情況下,所有其他線程的所有計算都被浪費了,因爲最終值由該線程給出。

這是一個簡單的例子。基本上,根本不能預測每個讀取和寫入mypi會發生什麼(y更容易,因爲所有線程都爲其分配相同的值)。

而且,當您在NumericRange上致電.par時,它會創建一個集合,其中包含所有NumericRange的值。

+0

感謝x * x在我翻譯C順序文件時沒有注意。我不確定爲什麼修改mypi並行應該是重要的,如果我沒有弄錯的話,整數的加法是原子的。很明顯,這通常不是好的做法,但我認爲在簡單的情況下這應該是可以的。有沒有一個建議可以解決這個問題,即我應該將所有中間結果存儲在一個列表中並對其進行總結? – Bbatha 2012-02-10 00:10:05

+0

讀int是原子,寫int是原子。讀取 - 修改 - 寫入不是原子的。 (查看丟失的更新。)第二個問題是mypi可以在本地緩存線程。 (請參閱java關鍵字volatile。)一種選擇是使用map-reduce算法。 – user482745 2012-02-29 09:07:06

-3

不知道底層的應用,我已經通過實驗證明,如果你使用的方法上Rangepar(例如)它提前被實例化,正如你所指出的教訓。

但是,它看起來像只使用集合來利用並行化。換句話說,要計算一段與集合本身無關的代碼 - 值i甚至沒有被使用。因此,foreach循環非常多餘,因爲您只感興趣的是y和x值。對於一個簡單的for-loop可以完成的事情來說,這看起來像是一項大量的工作。

表示其他類型的並行化在scala中非常容易。怎麼樣使用演員?它們輕巧而且非常簡單。否則工作線程或甚至Java線程可能會有所斬獲。

+0

呃,什麼? 'Range'不是一個「集合」,不管是「1到2」還是「1到1000000」都不會有更多的內存。我甚至不明白「僅使用集合來利用並行化」的含義:看起來循環對我來說是非常重要的算法! – 2012-02-09 11:37:31

+0

當然它是:)它只是一個集合,從一個值到另一個值。雖然我很遺憾不幸選擇了單詞,但我的意思是,如果使用方法par,那麼將導致scala運行並計算集合中的每個元素(Range或Stream中不是這種情況) 。關於「使用集合來利用並行化」,我想這很明顯,當方法par()被投入使用時。 Par就是簡單的scalas集合,它提供了創建線程的智能替代方案。因此集合的「優勢」。 – 2012-02-09 12:09:17