当要并行化的函数是列表元素时,foreach%dopar%产生“无法找到函数”错误

时间:2017-09-13 14:52:31

标签: r foreach doparallel

我已经成功并行化了一个函数 - 我们称之为AddOne - 通过doParallel包,foreach%dopar%,我熟悉{{1} {}}和.packages .export的参数。

我的问题是,我希望foreach而不是一个“独立”功能,成为列表的一个元素,在这种情况下,我无法让事情发生。具体来说,如果AddOne调用子例程AddOne,则“{工具”环境中找不到AddOneSubroutine,即使它已“导出”。

我正在使用Windows 10和AddOneSubroutine收益:

R.version

我的doParallel版本是1.0.10。 这里有一些代码可以尽可能简洁地演示这个问题。

platform       x86_64-w64-mingw32          
arch           x86_64                      
os             mingw32                     
system         x86_64, mingw32             
status                                     
major          3                           
minor          4.1                         
year           2017                        
month          06                          
day            30                          
svn rev        72865                       
language       R                           
version.string R version 3.4.1 (2017-06-30)
nickname       Single Candle 

我没理解什么?

1 个答案:

答案 0 :(得分:1)

为了在任何地方完全重现,让我们确保在后台R会话中使用工作人员:

library("doParallel")
cl <- parallel::makeCluster(detectCores(logical = TRUE))
registerDoParallel(cl)

现在,我还没有详细研究doParallel后端代码,所以我不能100%确定导致此问题的原因。但我们知道AddOneSubroutine确实已导出,如果您使用foreach(..., .verbose = TRUE),则可以看到,或者只是执行:

AddOneSubroutine <- function(x) { x + 1 }
y <- foreach(i = 1L, .export = "AddOneSubroutine") %dopar% {
    get("AddOneSubroutine")
}
str(y)
## List of 1
##  $ :function (x)  
##   ..- attr(*, "srcref")=Class 'srcref'  atomic [1:8] 1 20 1 40 20 40 1 1
##   .. .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x2e475a0> 

然而,当调用函数MyList$f()时,找不到它,可以通过使用确认:

AddOne <- function(x) exists("AddOneSubroutine")
MyList <- list()
MyList$f <- AddOne
y <- foreach(i = 1L, .export = "AddOneSubroutine") %dopar% {
    MyList$f(i)
}
str(y)
## List of 1
##  $ : logi FALSE

那么,为什么AddOneSubroutine不在MyList$f内搜索到的帧中呢?这可能是因为doParallel没有为MyList$f提供正确的环境。似乎有效的解决方法是以下黑客攻击:

AddOne <- function(x) { AddOneSubroutine(x) }
y <- foreach(i = 1L) %dopar% {
    environment(MyList$f) <- environment(AddOneSubroutine)
    MyList$f(i)
}
str(y)
## List of 1
##  $ : num 2

不幸的是,它不是很整洁也不方便。

作为替代方案,doFuture后端(我是作者)似乎效果稍好:

library("doFuture")
registerDoFuture()
plan(multisession)

AddOneSubroutine <- function(x) { x + 1 }
AddOne <- function(x) { AddOneSubroutine(x) }
MyList <- list()
MyList$f <- AddOne

y <- foreach(i = 1L) %dopar% {
    AddOneSubroutine ## dummy guiding auto-export
    MyList$f(i)
}
str(y)
## List of 1
##  $ : num 2

PS。您对特定用例感兴趣,因为理想情况下,{I}应该在使用doFuture时自动导出AddOneSubroutine,但事实并非如此。我已经在基础globals包中找到了解决方法(我是作者),但在发布之前我需要考虑更多。

我的详细信息:

> sessionInfo()
R version 3.4.1 (2017-06-30)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 16.04.3 LTS

Matrix products: default
BLAS: /usr/lib/atlas-base/atlas/libblas.so.3.0
LAPACK: /usr/lib/atlas-base/atlas/liblapack.so.3.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] parallel  stats     graphics  grDevices utils     datasets  methods  
[8] base     

other attached packages:
[1] doFuture_0.5.1  iterators_1.0.8 foreach_1.4.3   future_1.6.1   

loaded via a namespace (and not attached):
[1] compiler_3.4.1   tools_3.4.1      listenv_0.6.0    codetools_0.2-15
[5] digest_0.6.12    globals_0.10.2