[英]Parallel recursive function in R?
我整個星期都在為這個問題絞盡腦汁,並且真的可以使用外部視角。 基本上我已經構建了一個遞歸樹 function ,其中一層中每個節點的 output 用作后續層中節點的輸入。 我在這里生成了一個玩具示例,其中每個調用都會生成一個大矩陣,將其拆分為子矩陣,然后將這些子矩陣傳遞給后續調用。 與 Stack 上類似問題的主要區別在於,每次調用tree_search
實際上並不返回任何內容,它只是將結果附加到 CSV 文件中。
現在我想並行化這個 function。 但是,當我使用mclapply
和mc.cores=2
運行它時,運行時間會增加! 當我在帶有mc.cores=12
的多核集群上運行它時,也會發生同樣的情況。 這里發生了什么? 父節點是否在等待子節點返回一些output? 這與 fork/socket 並行化有關嗎?
作為背景,這是模擬白細胞中基因激活以響應病毒感染的算法的一部分。 我是一名生物學家和自學成才的程序員,所以我在這里有點超出我的深度 - 任何幫助或線索將不勝感激!
# Load libraries.
library(data.table)
library(parallel)
# Recursive tree search function.
tree_search <- function(submx = NA, loop = 0) {
# Terminate on fifth loop.
message(paste("Started loop", loop))
if(loop == 5) {return(TRUE)}
# Create large matrix and do some operation.
bigmx <- matrix(rnorm(10), 50000, 250)
bigmx <- sin(bigmx^2)
# Aggregate matrix and save output.
agg <- colMeans(bigmx)
append <- file.exists("output.csv")
fwrite(t(agg), file = "output.csv", append = append, row.names = F)
# Split matrix in submatrices with 100 columns each.
ind <- ceiling(seq_along(1:ncol(bigmx)) / 100)
lapply(unique(ind), function(i) {
submx <- bigmx[, ind == i]
# Pass each submatrix to subsequent call.
loop <- loop + 1
tree_search(submx, loop) # sub matrix is used to generate big matrix in subsequent call (not shown)
})
}
# Initiate tree search.
tree_search()
經過更多的腦筋急轉彎和實驗,我最終回答了我自己的問題。 我不打算參考原始示例,因為我已經改變了很多方法。 相反,我將分享一些可能對處於類似情況的人有所幫助的一般性觀察。
1.) For
循環比lapply
和遞歸函數更有效
使用 lapply 時,每次調用都會創建當前環境的副本。 這就是為什么你可以這樣做:
x <- 5
lapply(1:10, function(i) {
x <- x + 1
x == 6 # TRUE
})
x == 5 # ALSO TRUE
最后 x 仍然是 5,這意味着每次調用lapply
都在操作 x 的單獨副本。 如果 x 實際上是一個帶有 10,000 個變量的大型 dataframe,那就不好了。 另一方面, for
循環允許您覆蓋每個循環上的變量。
x <- 5
for(i in 1:10) {x <- x + 1}
x == 5 # FALSE
2.) 並行化一次
將任務分配到不同的節點需要大量的計算開銷,並且可以抵消您從並行化腳本中獲得的任何收益。 因此,您應該謹慎使用mclapply
。 就我而言,這意味着不要將mclapply
放在遞歸的 function 中,它會被調用數十到數百次。 相反,我將起點分成 16 個部分,並在不同的節點上運行 16 次不同的樹搜索。
3.) 您可以使用mclapply
來限制 memory 的使用
如果您將作業分成 10 個部分並使用mclapply
和mc.preschedule=F
處理它們,則每個核心一次只能處理 10% 的作業。 例如,如果mc.cores
設置為兩個,那么其他 8 個“節點”將等到一個部分完成后再開始一個新的部分。 如果您遇到 memory 問題並希望防止每個循環超出其處理能力,這將非常有用。
最后說明
這是迄今為止我研究過的更有趣的問題之一。 但是,遞歸樹函數很復雜。 畫出算法,強迫自己花幾天時間遠離你的代碼,這樣你就能以全新的視角回來。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.