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

Posted

技术标签:

【中文标题】parallel::mclapply() 添加或删除对全局环境的绑定。哪个?【英文标题】:parallel::mclapply() adds or removes bindings to the global environment. Which ones? 【发布时间】:2019-01-17 04:59:19 【问题描述】:

为什么这很重要

对于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 年 1 月 16 日创建

编辑

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

【问题讨论】:

【参考方案1】:

我认为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

【讨论】:

【参考方案2】:

您可以在锁定环境之前自行删除.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)

当然,这将不允许像rnorm 这样需要.Random.seed 的函数工作。

解决方法是将 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(没有进行太多测试)。您可以使用仅将其设置为 NULL 的函数覆盖删除 .Random.seed 的函数

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)

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

【讨论】:

好主意。不幸的是,这些方法不适合我的用例。 drake 专为可重现的伪随机性而设计。它自己设置.Random.seed,因此.Random.seed 绑定需要在环境被锁定之前已经存在。而且我强烈希望不理会 RNG 算法。 drake 适用于广泛而多样化的用户群,我认为人们希望 Mersenne Twister 成为默认设置。有些用户甚至可能想自己设置算法。 其实现在我重新阅读了你的问题,你已经回答了:它是.Random.seed。我回答试图为您遇到的问题提供解决方案。我认为您应该使用答案中的信息更改问题以询问特定问题。无论如何,我用另外两个想法编辑了我的答案,我认为这会对你有所帮助。 嗯...侵入推荐的软件包似乎太冒险了,无法在 drake 本身中实施或向用户推荐,但它是 excellent patch idea for base R。 虽然.Random.seed &lt;- NULL 应该是assign(".Random.seed", NULL, envir = .GlobalEnv) 关于使用 assign 的好处 - 编辑了我的答案。我认为对于drake,您可以使用我编写的最后一种方法,锁定并在新环境中工作。

以上是关于parallel::mclapply() 添加或删除对全局环境的绑定。哪个?的主要内容,如果未能解决你的问题,请参考以下文章

git merge 后打字稿更改未编译

在 foreach 循环中编辑字典值

开源 YDB 数据库

请介绍下电脑中的环境变量,它有啥用呢?

基于mysql+php111个人备忘录通讯录系统

Linux主机安全笔记