【问题标题】:Environment and scope when using parallel functions使用并行函数时的环境和范围
【发布时间】:2013-08-03 17:58:27
【问题描述】:

我有以下功能:

f1<-function(x){
  iih_data<-...stuff...
  ...more stuff...

  cl <- makeCluster(mc <- getOption("cl.cores", 6))
  clusterExport(cl, c("iih_data"))
  clusterEvalQ(cl, require(lme4))

  Tstar<-parCapply(cl, ystar, function(x){
     ostar=glmer(x ~ GENO + RACE + (1|GROUP), family="binomial",data=iih_data,nAGQ=1)
     fixef(ostar)[2]/sqrt(vcov(ostar)[2,2])
  })

  stopCluster(cl)

  ...more stuff...
}

但我收到此错误:

Error in get(name, envir = envir) : object 'iih_data' not found

我猜这与我试图在函数内部运行并行应用这一事实有关。你们能帮我解决这个问题吗?谢谢

【问题讨论】:

    标签: r function parallel-processing scope


    【解决方案1】:

    如您所见,clusterExport 会在 .GlobalEnv 中查找指定的变量,除非使用 envir 参数另有指示。但是在您的特定示例中,iih_data 与您使用parCapply 执行的未命名函数一起被序列化,因此您通过clusterExport 导出给工作人员的副本实际上不会被使用。实际上,在parCapply 执行之前在f1 中定义的所有局部变量都会与未命名的worker 函数一起序列化并发送给每个worker。

    这种技术对于向工作人员发送数据非常有用(它实际上由clusterExport 本身使用),但你必须知道你在做什么,否则它会严重影响你的性能,尤其是在使用@987654330 时@ 和 clusterApplyLB,因为它们不执行与 parLapplyparCapply 相同的预调度

    这里有一个简单的例子来说明这一点:

    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 创建的。

    这似乎是一个学术问题,但是一旦您开始从内部函数调用并行函数(例如 parLapplyclusterApply 以执行未命名的工作函数),它就会以各种形式出现。我被这种问题咬过很多次了。

    【讨论】:

    • 哇,多么出色而彻底的答案!谢谢!我知道发生了一些复杂的事情。
    • @Benjamin 我包含了有关未评估函数参数的内容,因为这是您在上一个问题中遇到的问题,可能被错误地标记为重复。寓意是:“小心并行执行闭包”。
    • 伙计,我希望我可以为我目前遇到的问题制作一个可重现的示例。我 1000% 确信是这种情况,但我不知道如何重现它。我有一个源脚本,在源脚本中有两个cl &lt;- makeCluster(n) parLapply(...some code...) stopCluster(n)。当并行部分发生时,会形成内存泄漏并填满系统内存。直接运行脚本内容无法重现
    【解决方案2】:

    添加

    envir=environment()
    

    到 clusterExport() 解决了这个问题。例如

    clusterExport(cl, c("iih_data"),envir=environment())
    

    【讨论】:

      猜你喜欢
      • 2018-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-19
      • 1970-01-01
      • 2012-09-17
      • 1970-01-01
      相关资源
      最近更新 更多