我应该优先使用Rcpp::NumericVector还是std::vector?

22

是否有任何理由让我更喜欢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的向量作为输入并返回向量作为输出,那么使用它们中的哪一个会有任何区别吗?使用std::vector<double>在与R交互时会导致任何可能的问题吗?


3
我不会说这完全是基于个人意见的。这个问题确实引发了一些担忧(无意中)。 - coatless
2个回答

30

考虑它们的工作和基准性能时,它们是等效的。

  1. 我怀疑基准测试的准确性,因为从一个SEXPstd::vector<double>需要将一个数据结构深度复制到另一个数据结构。 (当我输入这个时,@DirkEddelbuettel 进行了微基准测试。)
  2. Rcpp对象的标记(例如const Rcpp :: NumericVector& x)只是视觉上的语法糖。默认情况下,给定的对象是指针,因此可以轻松地具有涟漪修改效果(见下文)。因此,不存在真正存在于const std::vector<double>& x中的匹配项,可以有效地“锁定”并“传递引用”。

在与R交互时,使用std::vector<double>会导致任何可能的问题吗?

简而言之,没有。唯一付出的代价是对象之间的转移。

相对于此转移,其优点在于将分配给另一个NumericVectorNumericVector值的修改不会导致多个更新,实质上,每个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()添加属性)

3
好的回答。应该提到 RcppHoney 由@dcdillon提供了针对 std::vector<> 的语法糖。 - Dirk Eddelbuettel

24

“如果不确定,就计时。”

只需要在你已经有的文件中添加这几行代码:

/*** 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的比率。

实际上,如果你喜欢其中一种编码风格等等,可能并不重要。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接