如您所见,clusterExport 会在 .GlobalEnv 中查找指定的变量,除非使用 envir 参数另有指示。但是在您的特定示例中,iih_data 与您使用parCapply 执行的未命名函数一起被序列化,因此您通过clusterExport 导出给工作人员的副本实际上不会被使用。实际上,在parCapply 执行之前在f1 中定义的所有局部变量都会与未命名的worker 函数一起序列化并发送给每个worker。
这种技术对于向工作人员发送数据非常有用(它实际上由clusterExport 本身使用),但你必须知道你在做什么,否则它会严重影响你的性能,尤其是在使用@987654330 时@ 和 clusterApplyLB,因为它们不执行与 parLapply 和 parCapply 相同的预调度。
这里有一个简单的例子来说明这一点:
library(parallel)
cl <- makePSOCKcluster(3)
f1 <- function() {
iih_data <- 'foo'
parLapply(cl, 1:3, function(i) iih_data)
}
f1()
您可能会认为您会收到一条错误消息“找不到对象 'iih_data'”,因为您没有明确导出它,但您没有。奇怪的是,从全局环境中定义函数时不会发生这种情况,因为全局环境永远不会与函数一起序列化。
如果您认为这很奇怪,那么在处理争论时事情会变得很奇怪。考虑这个例子:
library(parallel)
cl <- makePSOCKcluster(3)
f1 <- function(iih_data) {
parLapply(cl, 1:3, function(i) iih_data)
}
x <- 'foo'
f1(x)
鉴于我之前的示例,您可能认为这可行,但您会收到以下错误:
Error in checkForRemoteErrors(val) :
3 nodes produced errors; first error: object 'x' not found
但是为什么它说“找不到对象'x'”而不是“找不到对象'iih_data'”?这是由于 R 对函数参数的惰性求值。该函数及其相关环境被序列化并发送给工作人员,而无需评估参数“iih_data”。直到在 worker 上执行未命名的 worker 函数时才会对其进行评估,那时它会发现 "x" 未在 worker 的全局环境中定义。
您可以通过将f1 更改为:
f1 <- function(iih_data) {
force(iih_data)
parLapply(cl, 1:3, function(i) iih_data)
}
如果您执行clusterExport(cl, 'iih_data', envir=environment()) 而不是调用force,它会起作用,但不是因为您已将其导出给工作人员。它会起作用,因为参数是强制的,但效率要低得多,并且复制到工人全局环境的值仍然不会被使用。工作函数实际上仍会使用本地环境中的“iih_data”副本,该副本是通过调用与未命名的工作函数一起序列化的f1 创建的。
这似乎是一个学术问题,但是一旦您开始从内部函数调用并行函数(例如 parLapply 和 clusterApply 以执行未命名的工作函数),它就会以各种形式出现。我被这种问题咬过很多次了。