RcppArmadillo和RcppParallel的同居

Elv*_*vis 5 rcpp

下面的玩具示例parallelFor工作正常(f2是并行版本f1):

// [[Rcpp::depends(RcppParallel)]]
// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
#include <RcppParallel.h>
#include <iostream>
#define vector NumericVector

using namespace Rcpp;
using namespace RcppParallel;


// compute values i/i+1 for i = 0 to n-1
// [[Rcpp::export]]
vector f1(int n) {
  vector x(n);
  for(int i = 0; i < n; i++) x[i] = (double) i/ (i+1);
  return x;
}

struct mytry : public Worker {
  vector output;

  mytry(vector out) : output(out) {}

  void operator()(std::size_t begin, std::size_t end) {
    for(int i = begin; i < end; i++) output[i] = (double) i/ (i+1);
  }

};

// [[Rcpp::export]]
vector f2(int n) {
  vector x(n);
  mytry A(x);
  parallelFor(0, n, A);
  return x;
}
Run Code Online (Sandbox Code Playgroud)

不过,如果我取代#define vector NumericVector#define vector arma::vec这不起作用了.代码编译并运行,没问题f1,但返回的向量f2只包含未初始化的值.

非常感谢任何澄清.

Kev*_*hey 8

这里的问题 - 你的类应该通过引用而不是值来获取向量.

这是因为,在使用时RcppParallel,您通常会在某处为对象预先分配内存,然后填充该对象 - 因此并行工作者应该引用您要填充的对象.

所以你的工人看起来应该像你所说的那样:

struct mytry : public Worker {
  vector& output;

  mytry(vector& out) : output(out) {}

  void operator()(std::size_t begin, std::size_t end) {
    for(int i = begin; i < end; i++) output[i] = (double) i/ (i+1);
  }
Run Code Online (Sandbox Code Playgroud)

请注意,这对Rcpp向量起作用(可能令人惊讶),因为它们只是"代理"对象 - 只是封装数据指针的对象.当您按值传递Rcpp向量时,您复制指针(不是基础数据!)加上一些额外的向量位(例如​​向量的长度) - 因此'copy'保留对同一数据结构的引用.

当你使用一个更"经典"的向量时,例如,arma::vec或者std::vector,当通过值传递给工作者时,你真的要将一个全新的向量复制到类中,然后填充那个(临时的,复制的)向量 - 所以原始的向量永远不会实际上已经填满