doParallel "foreach" 不一致地从父环境继承对象:"Error in { : task 1 failed - " 找不到函数...”
doParallel "foreach" inconsistently inherits objects from parent environment: "Error in { : task 1 failed - "could not find function..."
我无法弄清楚 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 的行为都应该像 for.
知道是什么原因造成的吗?是否有解决办法?
使用该功能的电脑版本信息:
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
@Tensibai 说的对。当尝试在 Windows 上使用 doParallel
时,您必须 "export" 您想要使用的不在当前范围内的函数。根据我的经验,我完成这项工作的方式是使用以下(已编辑)示例。
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
在 Linux 和 Mac OS X 上使用 mclapply
,但是 clusterApplyLB
在 Windows。这通常会导致代码在 Linux 上工作但在 Windows 上失败,因为由于 fork
而使用 mclapply
时,工作人员是主服务器的克隆。出于这个原因,我通常使用以下方法测试我的代码:
cl <- makePSOCKcluster(2)
registerDoParallel(cl)
确保我正在加载所有必要的包并导出所有必要的函数和变量,然后切换回 registerDoParallel(2)
以在支持它的平台上获得 mclapply
的好处。
请注意,当 doParallel
使用 mclapply
时,.packages
和 .export
选项将被忽略,但我建议始终使用它们以实现可移植性。
foreach 的 auto-export 特性在函数内部使用时效果不佳,因为 foreach 对于 auto-export 的内容相当保守。 auto-export 在当前环境中定义的变量和函数似乎很安全,但由于 R 的范围规则的复杂性,在这之外对我来说似乎有风险。
我倾向于同意你的评论,即你的两个 work-arounds 对于一个积极开发的包来说不是很稳定,但是如果 f
和 g
是在包 foo
,那么你应该使用 foreach .package
选项将包 foo
加载到 workers:
g <- function(){
r = foreach(x = 1:4, .packages='foo') %dopar% {
return(x + f())
}
return(r)
}
那么 f
将在 g
的范围内,即使它既没有被 foreach 隐式或显式导出。但是,这确实需要 f
是 foo
的导出函数(而不是内部函数),因为 worker 执行的代码没有在 foo
中定义,所以它只能访问导出的函数。 (很抱歉以两种不同的方式使用术语 "export",但这很难避免。)
我总是很想听听像您这样的评论,因为我一直在想是否应该调整 auto-export 规则。在这种情况下,我认为如果 foreach 循环由包中定义的函数执行,集群工作人员应该 auto-load 该包而不需要 .packages
选项。我会尝试研究一下,并可能将其添加到 doParallel
和 doSNOW
.
的下一个版本中
我无法弄清楚 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 的行为都应该像 for.
知道是什么原因造成的吗?是否有解决办法?
使用该功能的电脑版本信息:
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
@Tensibai 说的对。当尝试在 Windows 上使用 doParallel
时,您必须 "export" 您想要使用的不在当前范围内的函数。根据我的经验,我完成这项工作的方式是使用以下(已编辑)示例。
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
在 Linux 和 Mac OS X 上使用 mclapply
,但是 clusterApplyLB
在 Windows。这通常会导致代码在 Linux 上工作但在 Windows 上失败,因为由于 fork
而使用 mclapply
时,工作人员是主服务器的克隆。出于这个原因,我通常使用以下方法测试我的代码:
cl <- makePSOCKcluster(2)
registerDoParallel(cl)
确保我正在加载所有必要的包并导出所有必要的函数和变量,然后切换回 registerDoParallel(2)
以在支持它的平台上获得 mclapply
的好处。
请注意,当 doParallel
使用 mclapply
时,.packages
和 .export
选项将被忽略,但我建议始终使用它们以实现可移植性。
foreach 的 auto-export 特性在函数内部使用时效果不佳,因为 foreach 对于 auto-export 的内容相当保守。 auto-export 在当前环境中定义的变量和函数似乎很安全,但由于 R 的范围规则的复杂性,在这之外对我来说似乎有风险。
我倾向于同意你的评论,即你的两个 work-arounds 对于一个积极开发的包来说不是很稳定,但是如果 f
和 g
是在包 foo
,那么你应该使用 foreach .package
选项将包 foo
加载到 workers:
g <- function(){
r = foreach(x = 1:4, .packages='foo') %dopar% {
return(x + f())
}
return(r)
}
那么 f
将在 g
的范围内,即使它既没有被 foreach 隐式或显式导出。但是,这确实需要 f
是 foo
的导出函数(而不是内部函数),因为 worker 执行的代码没有在 foo
中定义,所以它只能访问导出的函数。 (很抱歉以两种不同的方式使用术语 "export",但这很难避免。)
我总是很想听听像您这样的评论,因为我一直在想是否应该调整 auto-export 规则。在这种情况下,我认为如果 foreach 循环由包中定义的函数执行,集群工作人员应该 auto-load 该包而不需要 .packages
选项。我会尝试研究一下,并可能将其添加到 doParallel
和 doSNOW
.