2015-07-10 29 views
0

我正在使用for循環計算置換測試統計信息。我希望使用並行處理來加速這一點(特別是foreach包中的foreach)。 https://beckmw.wordpress.com/2014/01/21/a-brief-foray-into-parallel-processing-with-r/在R中並行處理的foreach(foreach包)

我的原代碼:

library(foreach) 
library(doParallel) 
set.seed(10) 
x = rnorm(1000) 
y = rnorm(1000) 
n = length(x) 
nexp = 10000 
perm.stat1 = numeric(n) 
ptm = proc.time() 
for (i in 1:nexp){ 
    y = sample(y) 
    perm.stat1[i] = cor(x,y,method = "pearson") 
    } 
proc.time()-ptm 
# 1.321 seconds 

然而,當我用foreach循環,我得到的結果要慢得多:

cl<-makeCluster(8) 
registerDoParallel(cl) 
perm.stat2 = numeric(n) 
ptm = proc.time() 
perm.stat2 = foreach(icount(nexp), .combine=c) %dopar% { 
    y = sample(y) 
    cor(x,y,method = "pearson") 
} 
proc.time()-ptm 
stopCluster(cl) 
#3.884 seconds 

這是爲什麼我從下面的說明發生了什麼?我做錯了什麼? 謝謝

回答

0

foreach循環中有更多的計算開銷。這將返回一個列表,其中包含循環體的每次執行,然後通過參數.combine=c將其組合到一個向量中。 for循環不返回任何內容,而是將值賦給perm.stat1作爲副作用,所以不需要任何額外開銷。

看看Why is foreach() %do% sometimes slower than for?更深入的解釋爲什麼foreach在許多情況下比for慢。其中foreach進入它自己的是當循環內部的操作是計算密集型的,使得通過比較返回列表中的每個值無關緊要的時間罰分。例如,上面的Wordpress文章中使用的rnormsummary的組合。

1

你越來越糟糕的表現,因爲你分手了一個小問題變成萬任務,每個大約需要毫秒執行的第八。當循環的主體需要很長一段時間(我曾說過至少10秒,但我現在已經把它丟到現在至少一秒)時,簡單地將for循環變成foreach循環是可以的,但是,當任務很小時,簡單的策略不起作用(在這種情況下,非常小,很小)。當任務很小時,您大部分時間都會花費時間發送任務並接收工作人員的結果。換句話說,通信開銷大於計算時間。坦率地說,我很驚訝你沒有得到太多糟糕的表現。

對我來說,它並沒有真正似乎值得並行化需要不到兩秒鐘來執行的問題,但實際上你可以通過分塊得到加快使用foreach。也就是說,你把問題分成更小的塊,通常給每個工人一塊。這裏有一個例子:

nw <- getDoParWorkers() 
perm.stat1 <- 
    foreach(xnexp=idiv(nexp, chunks=nw), .combine=c) %dopar% { 
    p = numeric(xnexp) 
    for (i in 1:xnexp) { 
     y = sample(y) 
     p[i] = cor(x,y,method="pearson") 
    } 
    p 
    } 

正如你所看到的,foreach循環分裂問題成塊,並且循環體包含原始順序代碼的修改版本,現在在整個的一小部分工作問題。

在我的四核心Mac筆記本電腦上,它執行時間爲0.447秒,而順序版本爲1.245秒。這看起來像是一個非常可敬的速度。