我有一个foreach的问题,我无法搞清楚.以下代码在我尝试过的两台Windows计算机上失败,但在三台Linux计算机上运行成功,所有这些都运行相同版本的R和doParallel:
library("doParallel") registerDoParallel(cl=2,cores=2) f <- function(){return(10)} g <- function(){ r = foreach(x = 1:4) %dopar% { return(x + f()) } return(r) } g()
在这两台Windows计算机上,返回以下错误:
Error in { : task 1 failed - "could not find function "f""
但是,这在Linux计算机上运行得很好,并且使用%do%而不是%dopar%也可以正常工作,并且适用于常规for循环.
同样是变量,如设置真正的i <- 10
和更换return(x + f())
用return(x + i)
对于具有相同问题的其他人,有两种解决方法:
1)使用.export显式导入所需的函数和变量:
r = foreach(x=1:4, .export="f") %dopar%
2)导入所有全局对象:
r = foreach(x=1:4, .export=ls(.GlobalEnv)) %dopar%
这些变通方法的问题在于,对于一个积极开发的大型软件包来说,它们并不是最稳定的.无论如何,foreach应该表现得像.
是什么导致了这个以及是否有修复的想法?
该功能的计算机版本信息:
R version 3.2.2 (2015-08-14) Platform: x86_64-pc-linux-gnu (64-bit) Running under: CentOS release 6.5 (Final) other attached packages: [1] doParallel_1.0.10 iterators_1.0.8 foreach_1.4.3
该功能不起作用的电脑:
R version 3.2.2 (2015-08-14) Platform: x86_64-w64-mingw32/x64 (64-bit) Running under: Windows 7 x64 (build 7601) Service Pack 1 other attached packages: [1] doParallel_1.0.10 iterators_1.0.8 foreach_1.4.3
brittenb.. 11
@Tensibai是对的.尝试doParallel
在Windows 上使用时,必须"导出"要使用的不在当前范围内的功能.根据我的经验,我完成这项工作的方式是使用以下(编辑)示例.
format_number <- function(data) { # do stuff that requires stringr } format_date_time <- function(data) { # do stuff that requires stringr } add_direction_data <- function(data) { # do stuff that requires dplyr } parse_data <- function(data) { voice_start <- # vector of values voice_end <- # vector of values target_phone_numbers <- # vector of values parse_voice_block <- function(block_start, block_end, number) { # do stuff } number_of_cores <- parallel::detectCores() - 1 clusters <- parallel::makeCluster(number_of_cores) doParallel::registerDoParallel(clusters) data_list <- foreach(i = 1:length(voice_start), .combine=list, .multicombine=TRUE, .export = c("format_number", "format_date_time", "add_direction_data"), .packages = c("dplyr", "stringr")) %dopar% parse_voice_block(voice_start[i], voice_end[i], target_phone_numbers[i]) doParallel::stopCluster(clusters) output <- plyr::rbind.fill(data_list) }
由于前三个函数不包含在我当前的环境中,doParallel
因此在启动R的新实例时会忽略它们,但它会知道在哪里可以查找,parse_voice_block
因为它在当前范围内.此外,您需要指定在每个新的R实例中应该加载哪些包.正如Tensibai所说,这是因为您没有运行分支过程,而是同时启动R的多个实例并运行命令.
@Tensibai是对的.尝试doParallel
在Windows 上使用时,必须"导出"要使用的不在当前范围内的功能.根据我的经验,我完成这项工作的方式是使用以下(编辑)示例.
format_number <- function(data) { # do stuff that requires stringr } format_date_time <- function(data) { # do stuff that requires stringr } add_direction_data <- function(data) { # do stuff that requires dplyr } parse_data <- function(data) { voice_start <- # vector of values voice_end <- # vector of values target_phone_numbers <- # vector of values parse_voice_block <- function(block_start, block_end, number) { # do stuff } number_of_cores <- parallel::detectCores() - 1 clusters <- parallel::makeCluster(number_of_cores) doParallel::registerDoParallel(clusters) data_list <- foreach(i = 1:length(voice_start), .combine=list, .multicombine=TRUE, .export = c("format_number", "format_date_time", "add_direction_data"), .packages = c("dplyr", "stringr")) %dopar% parse_voice_block(voice_start[i], voice_end[i], target_phone_numbers[i]) doParallel::stopCluster(clusters) output <- plyr::rbind.fill(data_list) }
由于前三个函数不包含在我当前的环境中,doParallel
因此在启动R的新实例时会忽略它们,但它会知道在哪里可以查找,parse_voice_block
因为它在当前范围内.此外,您需要指定在每个新的R实例中应该加载哪些包.正如Tensibai所说,这是因为您没有运行分支过程,而是同时启动R的多个实例并运行命令.
当您注册doParallel
使用时,这是非常不幸的:
registerDoParallel(2)
然后doParallel
使用mclapply
在Linux和Mac OS X,但clusterApplyLB
在Windows上隐式创建群集对象.这往往会导致代码在Linux上工作,但由于工人使用时是主人的克隆Windows上无法mclapply
因fork
.出于这个原因,我通常使用以下方法测试我的代码
cl <- makePSOCKcluster(2) registerDoParallel(cl)
确保我正在加载所有必需的包并导出所有必需的函数和变量,然后切换回registerDoParallel(2)
以获得mclapply
支持它的平台的好处.
请注意,使用时会忽略.packages
和.export
选项,但我建议始终使用它们来实现可移植性.doParallel
mclapply
foreach的自动导出功能在函数内部使用时效果不是很顺利,因为foreach对自动导出的内容相当保守.自动导出当前环境中定义的变量和函数似乎是非常安全的,但由于R的范围规则的复杂性,在我之外似乎有风险.
我倾向于使用您的评论同意你的两种解决方法是不是一个积极的开发包非常稳定,但是如果f
和g
在包中定义foo
,那么你应该使用foreach .package
选项加载包foo
上的工人:
g <- function(){ r = foreach(x = 1:4, .packages='foo') %dopar% { return(x + f()) } return(r) }
然后f
将在范围内,g
即使它不是由foreach隐式或显式导出的.但是,这确实需要它f
是foo
(而不是内部函数)的导出函数,因为worker没有定义代码foo
,因此它只能访问导出的函数.(抱歉以两种不同的方式使用术语"导出",但很难避免.)
我总是有兴趣听到像你这样的评论,因为我总是想知道是否应该调整自动导出规则.在这种情况下,我认为如果foreach循环由包中定义的函数执行,集群工作者应该自动加载该包而不需要该.packages
选项.我将尝试研究它,并可能将其添加到下一版本doParallel
和doSNOW
.