2017-03-02 86 views
0

我在%dopar%並行化環境中嵌套多個級別do.call(各自使用參數中命名的函數,而非硬編碼) ,並且內部函數無法找到來自我的外部環境的函數。我知道關於foreach上的.export參數,並且正在使用它,但不知何故,指定的函數並未沿整個鏈傳播。在foreach%dopar%環境中嵌套的do.call找不到使用.export傳遞的函數

我減少了我的問題,下面的測試情況下,不出現此問題:

library(doParallel) 
cl <- makeCluster(4) 
registerDoParallel(cl) 

simple.func <- function(a, b) { 
    return(a+b) 
} 

inner.func <- function(a, b) { 
    return(do.call(simple.func, list(a=a, b=b))) 
} 

outer.func <- function(a, b, my.func=inner.func) { 
    return(do.call(my.func, list(a=a, b=b))) 
} 

main.func <- function(my.list=1:10, my.func=outer.func, 
         my.args=list(my.func=inner.func)) { 
    results <- foreach(i=my.list, .multicombine=TRUE, .inorder=FALSE, 
        .export="simple.func") %dopar% { 
    return(do.call(my.func, c(list(a=i, b=i+1), my.args))) 
    } 
    return(results) 
} 

而不是給出正確答案(有一些數字的列表),我得到:

Error in { : task 1 failed - "object 'simple.func' not found" 

if (!exists("simple.func")) stop("Could not find parse.data in scope main.func")添加到每個功能的開始處(根據需要更改示波器的名稱)顯示它是inner.func哪一個看不到simple.func - 即使outer.func確實是看到它。

我還測試了上述幾個變體,main.funcouter.func具有硬編碼的下一級功能,而不是從參數中使用它。這兩種變化工作(例如,給出預期的結果),但是對於真實世界的情況,我想保留將子功能作爲參數的普遍性。

# Variation number one: Replace main.func() with this version 
main.func <- function(my.list=1:10, my.func=outer.func, 
         my.args=list(my.func=inner.func)) { 
    results <- foreach(i=my.list, .multicombine=TRUE, .inorder=FALSE, 
        .export=c("simple.func", "outer.func", "inner.func")) %dopar% { 
    return(do.call(outer.func, list(a=i, b=i+1, my.func=inner.func))) 
    } 
    return(results) 
} 

# Variation number two: Replace outer.func() and main.func() with these versions 
outer.func <- function(a, b, my.func=inner.func) { 
    return(do.call(inner.func, list(a=a, b=b))) 
} 

main.func <- function(my.list=1:10, my.func=outer.func, 
         my.args=list(my.func=inner.func)) { 
    results <- foreach(i=my.list, .multicombine=TRUE, .inorder=FALSE, 
        .export=c("simple.func", "inner.func")) %dopar% { 
    return(do.call(my.func, c(list(a=i, b=i+1), my.args))) 
    } 
    return(results) 
} 

我也可以通過simple.func下來手動鏈條,通過包括它作爲一個額外的參數,但是這看起來額外的凌亂,並且它爲什麼要在必要時simple.func應該只是沿着與環境的一部分通過?

# Variation number three: Replace inner.func(), outer.func(), and main.func() 
# with these versions 
inner.func <- function(a, b, innermost.func=simple.func) { 
    return(do.call(innermost.func, list(a=a, b=b))) 
} 

outer.func <- function(a, b, my.func=inner.func, 
         innermost.args=list(innermost.func=simple.func)) { 
    return(do.call(my.func, c(list(a=a, b=b), innermost.args))) 
} 

main.func <- function(my.list=1:10, my.func=outer.func, 
         my.args=list(my.func=inner.func, 
         innermost.args=list(innermost.func=simple.func))) { 
    results <- foreach(i=my.list, .multicombine=TRUE, .inorder=FALSE, 
        .export="simple.func") %dopar% { 
    return(do.call(my.func, c(list(a=i, b=i+1), my.args))) 
    } 
    return(results) 
} 

有沒有人有較少的問題解決方案的想法,或這個問題的根本原因?

+0

由於您的代碼充滿了它們,所以只需要注意:所有'return'調用都是不必要的。 R自動返回函數中最後一個表達式的值。你只需要'return'來提前離開一個函數。 –

回答

0

對於doParallel,以及任何其他doNnn適配器不叉當前的過程中,我認爲以下破解會做到這一點:

main.func <- function(my.list = 1:10, my.func=outer.func, 
         my.args = list(my.func=inner.func)) { 
    results <- foreach(i = my.list, .multicombine = TRUE, .inorder = FALSE, 
        .export="simple.func") %dopar% { 
    environment(my.args$my.func) <- environment() ## <= HACK 
    return(do.call(my.func, args = c(list(a=i, b=i+1), my.args))) 
    } 
    return(results) 
} 

或者,你可以使用doFuture適配器(I」作者)。然後,您不必擔心全局對象,因爲它們會自動識別並導出。也就是說,不需要指定.export(或.packages)。例如,你的情況了以下工作:

library("doFuture") 
registerDoFuture() 
plan(multisession, workers = 4) 

main.func <- function(my.list = 1:10, my.func = outer.func, 
         my.args = list(my.func = inner.func)) { 
    foreach(i = my.list, .multicombine = TRUE, .inorder = FALSE) %dopar% { 
    do.call(my.func, args = c(list(a = i, b = i+1), my.args)) 
    } 
} 

res <- main.func(1:3) 
str(res) 
## List of 10 
## $ : num 3 
## $ : num 5 
## $ : num 7 

您也可以跳過foreach()一直做:

library("future") 
plan(multisession, workers = 4) 

main <- function(my.list = 1:10, my.func = outer.func, 
       my.args = list(my.func = inner.func)) { 
    future_lapply(my.list, FUN = function(i) { 
    do.call(my.func, args = c(list(a = i, b = i+1), my.args)) 
    }) 
} 

PS。有很多不同plan()後端可供選擇。唯一沒有涉及的是如果你使用doRedis

+0

環境()hack似乎工作,但使用doFuture庫有時會出現以下錯誤(運行計劃(cluster,workers = cl)加載羣集時):「UseMethod中的錯誤(」tweak「):沒有適用的方法適用於「功能」類的對象的「調整」。 有沒有一個簡單的答案,或者我應該打開一個新的問題(或向您的GitHub提交錯誤)? – Randall

+0

嗯......可能是你附加了另一個定義另一個'集羣'功能的包嗎? 'str(cluster)'說它屬於什麼?計劃(「集羣」,工人= cl)或計劃(future :: cluster,workers = cl)'修復它?如果沒有,請在https://github.com/HenrikBengtsson/future上創建一個關於此的問題,以便我們能夠弄清楚發生了什麼。 Thxs讓我知道。 – HenrikB