运行 使用 for 或 foreach 和带有 Rcpp 的 C++ 函数内存不足

Running out of Memory using for or foreach and a C++-function with Rcpp

我想生成一个大矩阵(大约 300.000 * 5000)并用来自给定观察向量的随机样本填充它。

R 代码如下所示:

library(foreach)
elements <- as.numeric(1:1000)

result_list <- foreach(i=(1:50)) %do% {
  mypackage::rddrawmatrixC2(n_bootstrap = 100, 
                             n_obs_censusdata = 300000,
                             elements_to_draw_from = elements))))
}

rddrawmatrixC2 是一个 C++ - 使用 Rcpp 导出的函数。我写了它,因为 sample() 似乎慢得多。

因为我正在优化速度,所以我尝试了不同的方法:

每个方法都会导致错误

cannot allocate Vector of n mb

n 在 1.6 mb 和 200 mb 之间变化。

我想这与我的功能无关,因为在这个简单的例子中发生了同样的事情,第一行工作正常,但循环没有:

m <- matrix(1:6000*5000, nrow = 6000, ncol = 5000)
result_list <- foreach(i=(1:50)) %do% {
  matrix(1:6000*5000, nrow = 6000, ncol = 5000)
}

据我所知,更改列表的项目不应导致整个列表在内部被复制,data.table 中的 := 运算符绝对应该避免访问不必要的信息。您有任何解释/解决方法吗?这可能是与 Rcpp 有关的问题吗?有没有一种方法可以使用 Rcpp 创建整个列表来避免问题,然后 return 列表而不会使 R 的内存再次崩溃?

我正在研究 Windows 10,R 3.4.4。如果需要其他信息,我很乐意提供。

非常感谢任何帮助(以及对我的代码的反馈),谢谢!

这是 C++ 的代码 - 函数:

#include <RcppEigen.h>
#include <random>

using namespace Rcpp;

// [[Rcpp::export]]
SEXP rddrawmatrixC2(const int n_bootstrap,
                    const Eigen::Map<Eigen::VectorXd> elements_to_draw_from, 
                    const int n_obs_censusdata)
{
  const int upper = elements_to_draw_from.size();
  std::random_device rd; // used to obtain a seed for the number engine
  std::mt19937 gen(rd()); // Mersenne Twister engine 
  std::uniform_int_distribution<> dis(1, upper);

  // initialise matrix that can be filled
  NumericMatrix returnmatrix(n_obs_censusdata, n_bootstrap);
  const int matrixsize = n_obs_censusdata * n_bootstrap;

  for (int i=0; i<matrixsize; ++i)
    returnmatrix[i] = elements_to_draw_from[dis(gen)-1]; // subtract 1 because in C++ indices start with 0

  return Rcpp::wrap(returnmatrix);
}

(这个函数还有一个版本没有使用Rcpp::Eigen,但错误是一样的)

编辑/补充: 问题显然不在于使用 for/foreach/Rcpp。更确切地说,即使一次只访问一小部分,R 仍然必须将整个列表/data.table 保存在内存中。一种解决方案是将数据写入文件或使用包 bigstatsr 中提供的基于文件的矩阵。

正如 Ralf Stubner 和 Florian Privé 指出的那样,好的起点是: https://privefl.github.io/blog/a-guide-to-parallelism-in-r/#filling-something-in-parallel https://github.com/privefl/bigstatsr

代表

elements <- as.numeric(1:1000)

my_fun <- function(n_bootstrap,
                   n_obs_censusdata,
                   elements_to_draw_from) {
  replicate(n_bootstrap, sample(elements_to_draw_from, n_obs_censusdata, TRUE))
}

FBM 的 Foreach 解决方案

library(bigstatsr)
X <- FBM(300000, 5000)

library(doParallel)
registerDoParallel(cl <- makeCluster(nb_cores()))
foreach(i = 1:50, .combine = 'c') %dopar% {
  cols <- 1:100 + (i - 1) * 100
  X[, cols] <- my_fun(n_bootstrap = 100,
                      n_obs_censusdata = 300000,
                      elements_to_draw_from = elements)
  NULL
}
stopCluster(cl)

注意 foreach returns 的东西,这就是我使用 NULL 的原因,因为我们只想在这里分配。

直接与big_apply

big_apply 为您处理拆分/并行。

big_apply(X, a.FUN = function(X, ind, my_fun, elements) {
  X[, ind] <- my_fun(n_bootstrap = length(ind),
                     n_obs_censusdata = 300000,
                     elements_to_draw_from = elements)
  NULL
}, a.combine = 'c', ncores = nb_cores(), block.size = 100,
my_fun = my_fun, elements = elements)