在Rcpp中决定NumericVector和arma :: vec

时间:2018-05-04 08:08:21

标签: r rcpp armadillo

使用RcppArmadillo,使用arma::vec从R到Rcpp的转换就像使用Rcpp和NumericVector一样简单。我的项目使用RcppArmadillo。

我不确定要使用什么,NumericVectorarma::vec?这两者之间的主要区别是什么?什么时候用哪个?使用一个优于另一个是否有性能/内存优势?成员职能的唯一区别是什么?并且,作为奖励问题:我是否应该考虑arma::colvecarma::rowvec

2 个答案:

答案 0 :(得分:17)

  

这两者之间的主要区别是什么?

Rcpp 中的*Vector*Matrix类充当 R SEXP 的包装器表示,例如 S 表达式,作为指向数据的指针。有关详细信息,请参阅Section 1.1 SEXPsR Internals Rcpp 的设计通过从包含指针的类构造 C ++ 对象来利用此功能对数据。这促进了两个关键特征:

  1. R C ++ 对象之间的无缝转换,以及
  2. R C ++ 之间的转移成本低,因为只传递指针。
    • 由于数据未经复制,但已引用
  3. 与此同时,arma对象类似于 R 之间副本的传统std::vector<T> C ++ 对象。这个语句有一个例外,即advanced constructor的存在,它允许 R 对象后面的内存<{>>重用在{{1}内对象的结构。因此,如果您不小心,在从 R 过渡到 C ++ 期间可能会受到不必要的惩罚,反之亦然。

    注意:允许重用内存的高级构造函数arma::sp_mat不存在 。因此,使用稀疏矩阵的引用可能产生所需的加速,因为从 R C ++ 并返回执行复制。

    您可以在很大程度上基于&#34;传递参考&#34;来查看差异。或者&#34; pass-by-copy&#34;范例。要了解代码之外的差异,请考虑mathwarehouse之后的以下GIF:

    为了在代码中说明这种情况,请考虑以下三个函数:

    armadillo

    假设相应的数据类型,两个函数#include <RcppArmadillo.h> // [[Rcpp::depends(RcppArmadillo)]] // [[Rcpp::export]] void memory_reference_double_ex(arma::vec& x, double value) { x.fill(value); } // [[Rcpp::export]] void memory_reference_int_ex(arma::ivec& x, int value) { x.fill(value); } // [[Rcpp::export]] arma::vec memory_copy_ex(arma::vec x, int value) { x.fill(value); return x; } memory_reference_double_ex()将更新 R 中的对象存在。因此,我们可以通过在其定义中指定memory_reference_int_ex()来避免返回值,因为void分配的内存正在重用。第三个函数x需要返回类型,因为它是逐个复制的,因此,在没有重新分配调用的情况下修改现有存储。

    强调:

    1. memory_copy_ex()向量将通过引用传递到 C ++ ,例如x&末尾的arma::vec&
    2. R 中的arma::ivec&类是xdouble,意味着我们匹配arma::vec的基础类型,例如{{ 1}},或arma::ivec,例如integer
    3. 让我们快速看看两个例子。

      在第一个示例中,我们将查看运行Col<double>的结果,并将其与Col<int>生成的结果进行比较。注意, R C ++ 中定义的对象之间的类型是相同的(例如memory_reference_double_ex())。在下一个示例中,这将保持。

      memory_copy_ex()

      现在,如果 R 对象的基础类型是double而不是x = c(0.1, 2.3, 4.8, 9.1) typeof(x) # [1] "double" x # [1] 0.1 2.3 4.8 9.1 # Nothing is returned... memory_reference_double_ex(x, value = 9) x # [1] 9 9 9 9 a = memory_copy_ex(x, value = 3) x # [1] 9 9 9 9 a # [,1] # [1,] 3 # [2,] 3 # [3,] 3 # [4,] 3 会发生什么?

      integer

      发生什么事了?为什么没有double更新?好吧,在幕后 Rcpp 创建了一个新的内存分配,它是正确的类型 - x = c(1L, 2L, 3L, 4L) typeof(x) # [1] "integer" x # [1] 1 2 3 4 # Return nothing... memory_reference_double_ex(x, value = 9) x # [1] 1 2 3 4 而不是x - 然后再将其传递到double。这引起了参考&#34;链接&#34;两个对象之间有所不同。

      如果我们更改为在int向量中使用整数数据类型,请注意我们现在具有相同的效果:

      armadillo

      这导致讨论这两种范式的有用性。由于 speed 是使用 C ++ 时的首选基准,所以让我们根据基准来看待这一点。

      考虑以下两个功能:

      armadillo

      在它们上面运行微基准测试产生:

      memory_reference_int_ex(x, value = 3)
      
      x
      # [1] 3 3 3 3
      

      enter image description here

      注意:每次迭代引用的对象比复制的范例快〜6.509771倍,因为我们必须重新分配并填充该内存。

        

      何时使用哪个?

      需要做什么?

      您是否只是想快速加速依赖循环但不需要严格的线性代数操作的算法?

      如果是这样,只需使用 Rcpp 即可。

      您是否尝试执行线性代数操作? 或者您希望在多个库或计算平台(例如MATLAB,Python,R,...)中使用此代码?

      如果是这样,你应该在 armadillo 中写下算法的关键,并设置适当的钩子,用 Rcpp将函数导出到 R

        

      使用其中一个是否具有性能/内存优势?

      是的,如前所述,肯定具有性能/内存优势。不仅如此,而且通过使用 RcppArmadillo ,您实际上在 Rcpp 上添加了一个额外的库,从而增加了整体安装空间,编译时间和系统要求(请参阅macOS构建的困境)。找出项目所需的,然后选择该结构。

        

      会员职能的唯一区别是什么?

      不仅是成员函数,还有:

      • 基于矩阵分解的估算程序
      • 计算统计数量值
      • 对象生成
      • 稀疏表示(避免操纵S4对象)

      Rcpp armadillo 之间的基本差异。一个是为了便于将 R 对象转移到 C ++ 中,而另一个用于更严格的线性代数计算。这应该是很明显的,因为 Rcpp 实现任何矩阵乘法逻辑,而#include <RcppArmadillo.h> // [[Rcpp::depends(RcppArmadillo)]] // [[Rcpp::export]] void copy_double_ex(arma::vec x, double value) { x.fill(value); } // [[Rcpp::export]] void reference_double_ex(arma::vec& x, double value) { x.fill(value); } 使用系统的基本线性代数子程序(BLAS)来执行计算。

        

      而且,作为一个额外的问题:我是否应该考虑arma :: colvec或arma :: rowvec?

      取决于您希望如何返回结果。您想拥有:# install.packages("microbenchmark") library("microbenchmark") x = rep(1, 1e8) micro_timings = microbenchmark(copy_double_ex(x, value = 9.0), reference_double_ex(x, value = 9.0)) autoplot(micro_timings) micro_timings # Unit: milliseconds # expr min lq mean median uq max neval # copy_double_ex(x, value = 9) 523.55708 529.23219 547.22669 536.71177 555.00069 640.5020 100 # reference_double_ex(x, value = 9) 79.78624 80.70757 88.67695 82.44711 85.73199 308.4219 100 (行向量)还是armadillo(列向量)?默认情况下,1 x N会将这些结构作为 matrix 对象返回其适当的维度,并且传统的1D R 向量。

      举个例子:

      N x 1

      测试:

      RcppArmadillo

答案 1 :(得分:4)

@coatless的答案是正确的,但是你没有要求提供细节。

与此同时,你的问题没有明确规定,因为你没有说明你需要什么载体。有了这个警告,我会说

  • 对于简单的用例,Rcpp很好,RcppArmadillo同样很好
  • 对于需要线性代数的用例,更喜欢RcppArmadillo
  • 表现将在很大程度上相同,但需要注意的是,如上所述,您希望RcppArmadillo明确地“逐个引用”
  • 只读向量访问(例如,像sum()min()或查找这样的减少)和读写访问之间也存在很大差异,其中重要的是如何返回修改后的向量< / LI>
  • 所有用例通常都比R代码快得多,所以在第一个例子中不要过于担心。

一旦你做对了,你就可以(也许应该)描述一下。