我应该更喜欢Rcpp :: NumericVector而不是std :: vector吗?

时间:2017-01-11 22:38:49

标签: c++ rcpp

有什么理由我更喜欢Rcpp::NumericVector而不是std::vector<double>

例如,下面的两个函数

// [[Rcpp::export]]
Rcpp::NumericVector foo(const Rcpp::NumericVector& x) {
  Rcpp::NumericVector tmp(x.length());
  for (int i = 0; i < x.length(); i++)
    tmp[i] = x[i] + 1.0;
  return tmp;
}

// [[Rcpp::export]]
std::vector<double> bar(const std::vector<double>& x) {
  std::vector<double> tmp(x.size());
  for (int i = 0; i < x.size(); i++)
    tmp[i] = x[i] + 1.0;
  return tmp;
}

在考虑其工作和基准性能时是等效的。我知道Rcpp提供了糖和矢量化操作,但是如果只是将R的向量作为输入并将向量作为输出返回,那么我使用哪一个会有什么区别吗?在与R?

交互时,使用std::vector<double>会导致任何可能出现的问题

2 个答案:

答案 0 :(得分:20)

  

在考虑其工作和基准性能时是等效的。

  1. 我怀疑基准测试是否准确,因为从SEXPstd::vector<double>需要从一个数据结构到另一个数据结构的副本。 (当我打字时,@ DirkEddelbuettel运行了一个微基准测试。)
  2. Rcpp对象的标记(例如const Rcpp::NumericVector& x)只是视觉糖。默认情况下,给定的对象是指针,因此很容易产生波纹修改效果(见下文)。因此,与const std::vector<double>& x存在的有效“锁定”和“传递引用”的真正匹配。
  3.   

    在与R?

    交互时,使用std::vector<double>会导致任何可能出现的问题

    简而言之,没有。唯一的惩罚是对象之间的转移。

    此转移的收益是修改分配给另一个NumericVector的{​​{1}}的值不会导致多米诺骨牌更新。实质上,每个NumericVector都是另一个std::vector<T>的直接副本。因此,以下情况不可能发生:

    #include<Rcpp.h>
    
    // [[Rcpp::export]]
    void test_copy(){
        NumericVector A = NumericVector::create(1, 2, 3);
        NumericVector B = A;
    
        Rcout << "Before: " << std::endl << "A: " << A << std::endl << "B: " << B << std::endl; 
    
        A[1] = 5; // 2 -> 5
    
        Rcout << "After: " << std::endl << "A: " << A << std::endl << "B: " << B << std::endl; 
    }
    

    给出:

    test_copy()
    # Before: 
    # A: 1 2 3
    # B: 1 2 3
    # After: 
    # A: 1 5 3
    # B: 1 5 3
    
      

    有什么理由我更喜欢Rcpp::NumericVector而不是std::vector<double>

    有几个原因:

    1. 如前所述,使用Rcpp::NumericVector可以避免 C ++ std::vector<T>之间的副本。
    2. 您可以访问 sugar 功能。
    3. 能够在 C ++中标记 Rcpp 对象(例如,通过.attr()添加属性)

答案 1 :(得分:13)

&#34;如果不确定,只需要时间。&#34;

只需将这几行添加到您已有的文件中即可:

/*** R
library(microbenchmark)
x <- 1.0* 1:1e7   # make sure it is numeric
microbenchmark(foo(x), bar(x), times=100L)
*/

然后只需调用sourceCpp("...yourfile...")就会生成以下结果(加上有符号/无符号比较的警告):

R> library(microbenchmark)

R> x <- 1.0* 1:1e7   # make sure it is numeric

R> microbenchmark(foo(x), bar(x), times=100L)
Unit: milliseconds
   expr     min      lq    mean  median      uq      max neval cld
 foo(x) 31.6496 31.7396 32.3967 31.7806 31.9186  54.3499   100  a 
 bar(x) 50.9229 51.0602 53.5471 51.1811 51.5200 147.4450   100   b
R> 

您的bar()解决方案需要制作副本才能在R内存池中创建 R 对象。 foo()没有。对于多次多次运行的大型向量非常重要。在这里,我们看到收盘率约为1.8。

在实践中,如果你喜欢一种编码风格而不是另一种编码风格可能并不重要。