40

我在这里问了一个相关的问题,反应很好: 使用并行的parLapply:无法访问并行代码中的变量

问题是当我尝试在函数内部使用答案时,它将无法正常工作,因为我认为它必须在clusterExport. 我已经阅读了小插图并查看了帮助文件,但我的知识库非常有限。我使用的方式parLapply我希望它的行为类似于lapply但它似乎没有。

这是我的尝试:

par.test <- function(text.var, gc.rate=10){ 
    ntv <- length(text.var)
    require(parallel)
    pos <-  function(i) {
        paste(sapply(strsplit(tolower(i), " "), nchar), collapse=" | ")
    }
    cl <- makeCluster(mc <- getOption("cl.cores", 4))
    clusterExport(cl=cl, varlist=c("text.var", "ntv", "gc.rate", "pos"))
    parLapply(cl, seq_len(ntv), function(i) {
            x <- pos(text.var[i])
            if (i%%gc.rate==0) gc()
            return(x)
        }
    )
}

par.test(rep("I like cake and ice cream so much!", 20))

#gives this error message
> par.test(rep("I like cake and ice cream so much!", 20))
Error in get(name, envir = envir) : object 'text.var' not found
4

2 回答 2

52

默认情况下clusterExport,在 中查找.GlobalEnv要导出的对象varlist。如果您的对象不在 中.GlobalEnv,您必须告诉clusterExport它在哪个环境中可以找到这些对象。

您可以将您的更改clusterExport为以下内容(我没有测试,但您在评论中说有效)

clusterExport(cl=cl, varlist=c("text.var", "ntv", "gc.rate", "pos"), envir=environment())

这样,它将在函数的环境中查找要导出的对象。

于 2012-08-19T01:09:48.613 回答
18

另一种解决方案是将附加变量作为参数包含在您的函数中;parLapply 也导出它们。如果 'text.var' 是大数据,那么将其作为应用于的参数而不是索引是值得的,因为这样只会导出与每个工作人员相关的 text.var 部分,而不是整个对象给每个工人。

par.test <- function(text.var, gc.rate=10){ 
    require(parallel)
    pos <-  function(i) {
        paste(sapply(strsplit(tolower(i), " "), nchar), collapse=" | ")
    }
    cl <- makeCluster(mc <- getOption("cl.cores", 4))
    parLapply(cl, text.var, function(text.vari, gc.rate, pos) {
        x <- pos(text.vari)
        if (i%%gc.rate==0) gc()
        x
    }, gc.rate, pos)
}

这在概念上也是令人愉悦的。(很少需要显式调用垃圾收集器)。

source()执行脚本时的内存管理会导致其他问题。比较

> stop("oops")
Error: oops
> traceback()
1: stop("oops")

在脚本中使用相同的调用

> source("foo.R")
Error in eval(ei, envir) : oops
> traceback()
5: stop("oops") at foo.R#1
4: eval(ei, envir)
3: eval(ei, envir)
2: withVisible(eval(ei, envir))
1: source("foo.R")

请记住,R 的serialize()函数在内部parLapply()用于将数据移动到工作人员,它将所有内容序列化到 .GlobalEnv。因此,在脚本中创建的数据对象被序列化到工作人员,而如果以交互方式运行,它们将不会被序列化。这可能解释了@SeldeomSeenSlim 在运行脚本时的问题。解决方案可能是更清楚地将“数据”与“算法”分开,例如,使用文件系统或数据库或......来存储对象。

于 2012-08-19T05:33:04.163 回答