这是出于好奇而提出的一般性问题。我正在使用
doParallel
包进行并行计算。我使用这些包来进行模拟。
我观察到,当我使用
foreach
循环进行模拟时,Rstudio 中的当前使用内存急剧上升(4+GiB),并且 Rstudio 有时崩溃了。
现在我转向
parallel::mclapply
并再次进行了相同的模拟,但令人惊讶的是没有问题,并且当前使用内存没有增加太多(10+MiB)。
我不明白代码内部发生了什么。我期待对上述过程的详细解释。
sessionInfo()
我的 R 是
R version 4.2.1 (2022-06-23) -- "Funny-Looking Kid"
Copyright (C) 2022 The R Foundation for Statistical Computing
Platform: aarch64-apple-darwin20 (64-bit)
操作系统是MacOS。
doParallel
软件包版本 1.0.17.
RStudio 版本 2023.03.01。
假设我们正在尝试计算 Erdos-Renyi 图的边数。我试图每次模拟图形并存储每次模拟的边计数值。
代码如下
#ER random graph generator
src1 <- {"#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericMatrix ER_AdjMatGEN_cpp(int N, double p){
NumericMatrix temp(N,N);
for(int i=0; i< N; i++){
for(int j=0; j < i; j++){
temp(i,j) = R::rbinom(1,p);
temp(j,i) = temp(i,j);
}
}
return temp;
}"}
Niter <- 10000
#________________
edgeCnt_result1 <- foreach(icount(Niter),
v = iter(function() ER_AdjMatGEN_cpp(10000, 0.3)),
.combine = "rbind") %dopar% {
sum(v)
}
#___________
edgeCnt_result1 <- do.call(rbind, mclapply(1:Niter, function(i) {v = ER_AdjMatGEN_cpp(N = 10000, 0.3)
return(sum(v))} , mc.cores = 7))
当我尝试运行第一次迭代时,Rstudio 崩溃,但当我运行第二次迭代时,它运行正常。
您观察到的
foreach()
和 mclapply()
之间的行为差异是由于它们处理内存和并行化的方式造成的。
foreach()
:R 中 foreach()
包中的 doParallel
函数是一个循环构造,提供 R 中标准 for
循环的并行版本。它使用 doParallel
包提供的并行后端,这是 foreach
包的“并行”适配器。 doParallel
包使用 parallel
包,它是基础 R 的一部分,提供并行执行功能。当您使用 foreach()
时,每个工作进程都会获得自己的数据副本。正如您所观察到的,如果您拥有大型数据集,这可能会导致内存使用量较高。
mclapply()
:R 中 mclapply()
包中的 parallel
函数在列表或向量上并行应用函数。它使用分叉进程来实现并行性,这是类 Unix 系统(包括 MacOS 和 Linux)上可用的方法。当您使用 mclapply()
时,分叉进程与父进程共享相同的内存空间,这可以导致较低的内存使用量。但是,这也意味着子进程中所做的任何更改都不会影响父进程。
在您的情况下,
foreach()
循环正在为每个工作进程创建数据副本,导致内存使用率较高并导致 RStudio 崩溃。另一方面,mclapply()
使用共享相同内存空间的分叉进程,从而降低内存使用量。
这里有一些建议:
foreach()
,请考虑减少每个工作进程需要处理的数据大小。mclapply()
,请注意它使用分叉进程,这可能存在一些限制和潜在问题,特别是当您在子进程中修改数据时。future
和 furrr
包,它们为并行计算提供更灵活、更强大的选项。以下是如何在 R 中使用
foreach()
和 mclapply()
的一些示例:
# Load the necessary libraries
library(doParallel)
library(parallel)
# Register the parallel backend for foreach
cl <- makeCluster(detectCores())
registerDoParallel(cl)
# Example using foreach
result_foreach <- foreach(i = 1:10, .combine = c) %dopar% {
# Some computation here
sqrt(i)
}
# Stop the cluster
stopCluster(cl)
# Example using mclapply
result_mclapply <- mclapply(1:10, function(i) {
# Some computation here
sqrt(i)
}, mc.cores = detectCores())
在这两个示例中,我们并行地将平方根函数应用于数字 1 到 10。
foreach()
示例使用doParallel
包来设置并行后端,而mclapply()
示例直接使用parallel
包。
后续步骤可能包括:
foreach()
和 mclapply()
的工作原理。future
和 furrr
包。