如您所见,除非参数另有指示,否则会clusterExport
在其中查找指定的变量。但是在您的特定示例中,它与您正在执行的未命名函数一起被序列化,因此您导出给工作人员的副本实际上不会被使用。事实上,在执行之前定义的所有局部变量都将与未命名的工作函数一起序列化并发送给每个工作人员。.GlobalEnv
envir
iih_data
parCapply
clusterExport
f1
parCapply
这种技术对于向工作人员发送数据非常有用(它实际上是clusterExport
自己使用的),但是你必须知道你在做什么,否则它会严重影响你的性能,尤其是在使用clusterApply
and时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)
}
如果不是调用force
你执行clusterExport(cl, 'iih_data', envir=environment())
,它会工作,但不是因为你已经将它导出给工人。它会起作用,因为该参数是强制的,但效率要低得多,并且复制到工人全局环境的值仍然不会被使用。工作函数实际上仍将使用本地环境中的“iih_data”副本,该副本是通过f1
与未命名的工作函数一起序列化的调用创建的。
这似乎是一个学术问题,但是一旦您开始调用并行函数(例如从内部函数)以执行未命名的工作函数parLapply
,它就会以各种形式出现。clusterApply
我已经被这种问题咬过很多次了。