平行化射飛鏢求pi過程所遇到的隨機問題
26 Oct 2019 Paragraph : 3132 words.Parallel Programming的第一次作業,看懂老師提供的參考code並照著上課投影片的平行化方式原本應該很順利的,最後在random的地方卡很久QAQ!!!
執行結果:
core\執行次數 | 10M | 100M | 1000M |
---|---|---|---|
1 | 0.138s | 1.263s | 12.550s |
2 | 0.076s | 0.679s | 6.686s |
4 | 0.045s | 0.363s | 3.534s |
遇到的主要問題
為什麼用越多thread,所花的時間反而越久?
在求助於BK大神後,這是餵狗找出的答案,原來以前也有學長遇過類似的問題。
大神解答
算是又學到了一課,以下是歸納筆記:
我原本使用的是srand()
這個取亂數函式,他跟最基本的rand()
差別在於rand()
是從0~MAX中隨機選一個整數,再由第n-1次取的亂數去生成第n次取的亂數,而rand()
都是從0開始的,所以重複跑這個程式會發現出現的順序是固定的。srand()
則會去改變初始的0,但也不完全是隨機取,他需要一個seed去決定要改成什麼,相同的seed就可以得到相同的結果,因此常會將seed設成Time,也就是現在的時間,因為時間會不斷地改變,所以每次執行程式就可以得到一個無法預期的隨機值。
使用srand()
雖然解決上述問題,但會遇到越多thread反而跑越慢的現象,根據大神的解答,srand()
應該是保有rand()
的另一個特性–lock
,本意是不希望在跑平行化程式時,跑在不同core上的thread同時呼叫rand的共用data,因為這樣可能會得到一樣的隨機值,因此把它lock住讓共用data一次只能由一個thread讀,因此不同的thread在需要取亂數時都要等另一個thread讀完,會多出很多等待的時間,尤其是thread越多排隊的人就越長,花的時間就越久。
rand_r
可以解決上述問題,跟前面不同的是,srand()
只需要在函式內設定一次seed就好,而rand_r
每次呼叫時都要傳入一個seed,由於每次呼叫都用一個新的seed,不會和其他thread共用,也就沒有lock的問題了。
原本的版本:
srand((unsigned int)time(NULL));
for (toss = 0; toss < my_toss; toss++) {
x = ((float)rand())/RAND_MAX;
y = ((float)rand())/RAND_MAX;
distance_squared = x*x + y*y;
if (distance_squared <= 1)
incircle++;
}
修正後的版本:
unsigned int seed = time(NULL);
for (toss = 0; toss < my_toss; toss++) {
x = (double)rand_r(&seed)/RAND_MAX;
y = (double)rand_r(&seed)/RAND_MAX;
distance_squared = x*x + y*y;
if (distance_squared <= 1)
incircle++;
}