這是如何將問題分解成碎片的一個很好的例子,它可以真正影響並行計算的好處。我遺漏了一些代碼(例如,記錄協變量的變換以及消除缺失值),這對於這個問題並不重要。我想你應該避免在每次調用時調換整個矩陣 - 只需在腳本的頂部執行一次即可。 AFAIK R按列的主要順序存儲數據,所以通過在列上工作避免這一步可能已經爲您節省了一些時間。
在第一個試驗中,我首先運行了一個版本,看看它有多少改進。這是一款2.5 GHz的AMD Phenom 9850四核處理器,擁有8 GB的RAM(如此之久)。
library(doParallel)
library(iterators)
#make covariate data
N = 100
P = 100000 # number of predictors
tt = as.data.frame(matrix(rnorm(N*P),nrow=N,ncol=P))
my.y = rbinom(N,p=0.5,size=1)
y = factor(my.y)
# How fast to do it serially?
system.time(x1 <- foreach(cc = iter(tt, by='col'),.combine=c) %do% {
deviance(glm(y~cc, family = "binomial"))
}) # elapsed 718 s
nm.cores = detectCores() - 1
cl=makeCluster(nm.cores)
registerDoParallel(cl)
# send entire dataframe to each worker, pull out the desired column
system.time(x2 <- foreach(cnt=1:ncol(tt),.combine=c) %dopar% {
whol.dat = data.frame(tt[,cnt], y=factor(my.y))
deviance(glm(y~., data = whol.dat, family = "binomial"))
}) # elapsed 276 s, so 3 x faster
all.equal(x1,x2) # TRUE, just checkin' ...
我首先想到的是,整個矩陣,每次發送給每個工人可能攜帶一些開銷,所以我改寫了foreach()
使用iter()
只是每一列發送到工人:
system.time(x3 <- foreach(cc = iter(tt, by='col'),.combine=c) %dopar% {
deviance(glm(y~cc, family = "binomial"))
}) # not much faster, 248s
這確實加快了一些,但並不多。我之前沒有使用過迭代器,所以在閱讀foreach時,我遇到了一個自定義迭代器iblkcol()
,它將data.frame分解爲塊,併發送每個塊以節省數據調度和從工作人員返回的開銷。該代碼爲is hidden away on Github(見行199-218)。
## from vignette on foreach:
## use iblkcol() instead of iter in loop to send blocks of columns instead of one at a time
system.time(x4 <- foreach(cc = iblkcol(tt, chunks = nm.cores),.combine=c,.packages='foreach') %dopar% {
foreach(x = 1:col(cc),.combine=c) %do% {
deviance(glm(y~cc[,x], family = "binomial"))
}
}) # 193 s!
這對於逐列發送每一列是一個很大的改進。我認爲可以通過調用glm()來調用一些額外的加速,以充分利用大部分模型幀從一次調用重用到另一次調用的事實。對wilcoxon()的調用應該是一樣的。
你能否更新你的回答如何在存在障礙因子的情況下做出回答,例如glm(y〜cc [,x] | team + week,family =「binomial」)。我在問,因爲在每一次迭代中,我們都需要持有「周」和「團隊」信息。 – user702846
我並不完全確定阻塞因子的含義,但如果因素團隊和周在大數據框的列中保持不變,請在glm()的調用中使用它們,如glm(y〜cc [,x ] *團隊+周,家庭=二項) – atiretoo