作为S4对象的一部分返回时,避免复制Armadillo矩阵

时间:2017-03-27 22:24:17

标签: c++ r rcpp

我有一个Rcpp函数,它接受一个S4光栅对象,将一些数据插入一个插槽,然后返回该对象的新版本。这是一个最小的代表:

#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]

// [[Rcpp::export]]
Rcpp::S4 new_raster(Rcpp::S4 raster, int n_elem) {
  Rcpp::S4 r_data(raster.slot("data"));
  r_data.slot("values") = Rcpp::NumericVector(n_elem);        

  // Need help with...
  // creating armadillo vector from `r_data.slot("values")` here
  // arma::vec new_data = ... ?

  return raster;
}

/*** R
library("raster")
r <- raster(res = 0.1)
n <- ncell(r)
r1 <- new_raster(r, n)
head(r1@data@values)
*/

由于返回的数据可能非常大,我想避免创建多个矢量副本。如何在raster@data@values插槽中创建犰狳矢量而不复制?

2 个答案:

答案 0 :(得分:4)

编辑:我没有正确理解这个问题。目标是从S4对象中提取信息并与Armadillo对象共享该内存。我将离开这个答案的第一部分,因为它仍然具有优点,因为它突出了如何重用Armadillo对象内存并重新分配给NumericVector。尽管如此,第二部分可能更为相关,因为在Noam的回应之后,现在问题已经明确了。

如果我正确理解了这个问题,目标是重用已分配给初始Armadillo向量的存储位置(例如零填充)。根据评论的子目标是稍后将其移至NumericVector

所以,我们走了:

#include<RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]    

// [[Rcpp::export]]
Rcpp::NumericVector vec_mem_ex(int n_elem) {

  // Make the initial vector
  arma::vec X(n_elem, arma::fill::zeros);

  // Create a new vector
  arma::vec Y(X.begin(), X.n_elem, false, true);
  // `copy_aux_mem` is set to false, the vector will instead directly
  // use the auxiliary memory (ie. no copying). 
  // This is dangerous in certain circumstances!

  // `strict` is set to true, the vector will be bound to the
  // auxiliary memory for its lifetime; the number of elements 
  // in the vector can't be changed

  // Show memory is shared by modifying value
  Y.fill(42.0);

  // Convert X to a NumericVector
  Rcpp::NumericVector Z = Rcpp::NumericVector(X.begin(), X.end());

  return Z;
}

/***R
(a = vec_mem_ex(5))
*/

这给出了:

> (a = vec_mem_ex(5))
[1] 42 42 42 42 42

修改

要捕获问题的S4 - 并通过@noam-ross'答案给出更新,请考虑以下事项:

#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]

// [[Rcpp::export]]
Rcpp::S4 new_raster(Rcpp::S4 raster, int n_elem) {

  // Embed S4 object for nesting
  Rcpp::S4 r_data(raster.slot("data"));

  // Create obj@data@values
  // Initializes values with a vector of 0's.
  r_data.slot("values") = Rcpp::NumericVector(n_elem);


  // --- The new part...

  // We do _not_ have access to the vector that was stored in r_data.slot("values")

  // Convert from SEXP to NumericVector
  Rcpp::NumericVector temp = Rcpp::NumericVector(r_data.slot("values"));

  // Use the advanced vector ctor of Armadillo to capture the memory location
  arma::vec new_data(
      temp.begin(), // Uses the iterator interface to access the double* requirement
      n_elem,       // Set the size of the vector
      false,        // Avoid copying by disabling `copy_aux_mem`
      true          // Bind memory by enabling `strict`
  );

  // Show memory is shared by modifying value
  new_data.fill(42.0);

  // --- End new

  return raster;
}

/*** R
library("raster")
r <- raster(res = 0.1)
n <- ncell(r)
r1 <- new_raster(r, n)
head(r1@data@values)
*/

答案 1 :(得分:1)

我通过向插槽分配一个空的但是正确大小的NumericVector来解决这个问题,然后在其上调用as()以找到使用高级Armadillo构造函数的内存指针,如@DirkEddelbuettel和@coatless建议的那样。

#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]

// [[Rcpp::export]]
Rcpp::S4 new_raster(Rcpp::S4 raster, int n_elem) {
  Rcpp::S4 r_data(raster.slot("data"));
  r_data.slot("values") = Rcpp::NumericVector(n_elem);
  arma::vec new_data(
      Rcpp::as<Rcpp::NumericVector>(r_data.slot("values")).begin(),
      n_elem, false, true
  );
    new_data.randn();
    return raster;
}

/*** R
library("raster")
r <- raster(res = 0.1)
n <- ncell(r)
r1 <- new_raster(r, n)
head(r1@data@values)
*/

结果:

#>[1] -0.26417159 -0.89250080  2.02276338  2.01164847  0.45227281 -0.09313601