parallel :: mclapply()添加或删除到全局环境的绑定。哪个?

时间:2019-01-17 04:59:19

标签: r mclapply

这为什么重要

对于drake,我希望用户能够在锁定的全局环境中执行mclapply()调用。为了重现性,环境被锁定。 Without locking, data analysis pipelines could invalidate themselves

mclapply()添加或删除全局绑定的证据

set.seed(0)
a <- 1

# Works as expected.
rnorm(1)
#> [1] 1.262954
tmp <- parallel::mclapply(1:2, identity, mc.cores = 2)

# No new bindings allowed.
lockEnvironment(globalenv())

# With a locked environment
a <- 2 # Existing bindings are not locked.
b <- 2 # As expected, we cannot create new bindings.
#> Error in eval(expr, envir, enclos): cannot add bindings to a locked environment
tmp <- parallel::mclapply(1:2, identity, mc.cores = 2) # Unexpected error.
#> Warning in parallel::mclapply(1:2, identity, mc.cores = 2): all scheduled
#> cores encountered errors in user code

reprex package(v0.2.1)于2019-01-16创建

编辑

有关最初的激励问题,请参见https://github.com/ropensci/drake/issues/675https://ropenscilabs.github.io/drake-manual/hpc.html#parallel-computing-within-targets

2 个答案:

答案 0 :(得分:4)

我认为parallel:::mc.set.stream()有答案。显然,mclapply()会默认尝试从全局环境中删除.Random.seed。由于默认的RNG算法是Mersenne Twister,因此我们将深入探讨下面的else块。

> parallel:::mc.set.stream
function () 
{
    if (RNGkind()[1L] == "L'Ecuyer-CMRG") {
        assign(".Random.seed", get("LEcuyer.seed", envir = RNGenv), 
            envir = .GlobalEnv)
    }
    else {
        if (exists(".Random.seed", envir = .GlobalEnv, inherits = FALSE)) 
            rm(".Random.seed", envir = .GlobalEnv, inherits = FALSE)
    }
}
<bytecode: 0x4709808>
<environment: namespace:parallel>

我们可以使用mc.set.seed = FALSE使以下代码正常工作,但这实际上并不是一个好主意。

set.seed(0)
lockEnvironment(globalenv())
parallel::mclapply(1:2, identity, mc.cores = 2, mc.set.seed = FALSE)

我想知道是否有一种方法可以锁定环境,同时仍然允许我们删除.Random.seed

答案 1 :(得分:3)

您可以在锁定环境之前自行删除.Random.seed。另外,您需要加载库(或之前使用函数)并将tmp分配给某些内容。

library(parallel)
tmp <- NULL
rm(".Random.seed", envir = .GlobalEnv, inherits = FALSE)
lockEnvironment(globalenv())
tmp <- parallel::mclapply(1:2, identity, mc.cores = 2)

当然,这将不允许需要.Random.seed之类的rnorm的功能才能工作。

一种解决方法是将RNG类型更改为“ L'Ecuyer-CMRG”,另请参见此处?nextRNGStream

library(parallel)
tmp <- NULL
RNGkind("L'Ecuyer-CMRG")
lockEnvironment(globalenv())
tmp <- parallel::mclapply(1:2, rnorm, mc.cores = 2)

编辑

我想到了您的问题的另一种解决方案,并且我认为这可以与任何RNG一起使用(没有做太多测试)。您可以使用将.Random.seed设置为NULL

的功能覆盖该功能。
library(parallel)
mc.set.stream <- function () {
  if (RNGkind()[1L] == "L'Ecuyer-CMRG") {
    assign(".Random.seed", get("LEcuyer.seed", envir = RNGenv), 
           envir = .GlobalEnv)
  } else {
    if (exists(".Random.seed", envir = .GlobalEnv, inherits = FALSE)) {
      assign(".Random.seed", NULL, envir = .GlobalEnv)
    }  
  }
}

assignInNamespace("mc.set.stream", mc.set.stream, asNamespace("parallel"))
tmp <- NULL
set.seed(0)
lockEnvironment(globalenv())
tmp <- parallel::mclapply(1:2, rnorm, mc.cores = 2)

最后一个想法:您可以创建一个新环境,其中包含您不想更改的所有内容,将其锁定并在其中工作。