问题
我正在try 实现一个可重现的多核模拟,并获得不一致的结果.请帮助我解释这些结果,并建议我实施这一点的正确方法.请注意,我正在研究WSL2(不过,我希望我的结果还有其他原因).
细节
每个并行任务生成随机数,并被动态分配到一个可用的核(与预先调度到核相反).根据documentation,这可以通过使用parallel::mclapply
中的mc.preschedule=FALSE
来实现.为了保证可重复性,在设置随机种子时,任务应独立于分配给它们的 node 生成相同的随机数.
try 的解决方案
我的 idea 是使用RNGkind("L'Ecuyer-CMRG")
和parallel::nextRNGStream
for each task分配一个单独的(独立的)随机数流.以下代码段生成与这些流关联的种子列表,每个任务有一个列表条目.
library(parallel)
RNGkind("L'Ecuyer-CMRG")
n <- 100 # number of tasks
set.seed(1)
seeds <- list(.Random.seed)
for (i in 2:n) {
seeds[[i]] <- nextRNGStream(seeds[[i - 1]])
}
现在的 idea 是,每个任务在开始生成随机数之前设定它的种子.我使用函数f
来表示一些任务.
f <- function(i, seeds) {
.Random.seed <- seeds[[i]]
rnorm(1)
}
结果不一致
我希望任务的结果与parallel::mclapply
中的参数mc.set.seed
无关,因为这些任务无论如何都会设置自己的种子.然而,正如在这里可以观察到的那样,情况并非如此:
cores <- 2 # set to more than one
r1 <- mclapply(1:n, f, seeds=seeds, mc.preschedule=FALSE, mc.cores=cores, mc.set.seed=TRUE)
r2 <- mclapply(1:n, f, seeds=seeds, mc.preschedule=FALSE, mc.cores=cores, mc.set.seed=FALSE)
cat("r1: ", sum(unlist(r1)), "\n")
cat("r2: ", sum(unlist(r2)), "\n")
# r1: 24.39407
# r2: 46.08108
此外,我预计这些任务将生成相同的随机数,无论它们是串行执行还是并行执行.情况也并非如此:
r3 <- mclapply(1:n, f, seeds=seeds, mc.preschedule=FALSE, mc.set.seed=FALSE, mc.cores=1)
cat("r3: ", sum(unlist(r3)), "\n")
# r3: -7.079515
为什么会出现这些结果,实施这一结果的正确方式是什么?