gpt4 book ai didi

r - 在 Rcpp 中决定 NumericVector 和 arma::vec

转载 作者:行者123 更新时间:2023-12-03 14:31:00 24 4
gpt4 key购买 nike

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

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

最佳答案

What are the key differences between those two?


*Vector*Matrix Rcpp 中的类充当 R 的 SEXP 表示的包装器,例如作为指向数据的指针的 S 表达式。详情请见 Section 1.1 SEXPsR Internals .Rcpp 的设计通过从包含指向数据的指针的类构造 C++ 对象来利用这一点。这促进了两个关键特性:
  • R 和 C++ 对象之间的无缝转移,以及
  • R 和 C++ 之间的传输成本低,因为只传递一个指针。
  • 因为数据不是复制而是引用

  • 同时, arma对象类似于传统的 std::vector<T>以在 R 和 C++ 对象之间发生深拷贝的方式。此语句有一个异常(exception),即 advanced constructor 的存在。 ,这允许 R 对象后面的内存为 重复使用 内部 armadillo对象的结构。因此,如果您不小心,您可能会在从 R 到 C++ 的转换过程中招致不必要的惩罚,反之亦然。

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

    您可以在很大程度上基于“传递引用”或“传递复制”范式来查看差异。要了解代码之外的差异,请考虑以下来自 mathwarehouse 的 GIF :



    为了在代码中说明这种情况,请考虑以下三个函数:
    #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()memory_reference_int_ex()假设存在适当的数据类型,将更新 R 中的对象。因此,我们可以通过指定 void 来避免返回值。在它们的定义中,因为 x 分配的内存正在被重用。第三个函数, memory_copy_ex()需要返回类型,因为它是按副本传递的,因此不会在没有重新分配调用的情况下修改现有存储。

    强调:
  • x vector 将通过引用传递到 C++ 中,例如&arma::vec&arma::ivec& , 和
  • x类(class)在 R 中是 doubleinteger这意味着我们正在匹配 arma::vec 的基础类型,例如 Col<double> , 或 arma::ivec ,例如Col<int> .

  • 让我们快速看一下两个例子。

    在第一个示例中,我们将查看运行 memory_reference_double_ex() 的结果。并将其与 memory_copy_ex() 生成的结果进行比较.请注意,在 R 和 C++ 中定义的对象之间的类型是相同的(例如 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

    现在,如果 R 对象的基础类型是 integer 会发生什么?而不是 double ?
    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得到更新?好吧,Rcpp 在幕后创建了一个正确类型的新内存分配—— double而不是 int -- 在将其传递给 armadillo 之前.这导致两个对象之间的引用“链接”不同。

    如果我们更改为在 armadillo 中使用整数数据类型向量,注意我们现在有之前给出的相同效果:
    memory_reference_int_ex(x, value = 3)

    x
    # [1] 3 3 3 3

    这导致了关于这两种范式的有用性的讨论。由于速度是使用 C++ 时的首选基准,让我们从基准的角度来看待它。

    考虑以下两个函数:
    #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);
    }

    对它们运行微基准测试会产生:
    # 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

    enter image description here

    注意:引用对象每次迭代比复制范式快约 6.509771 倍,因为我们不必重新分配和填充该内存。

    When to use which?



    你需要做什么?

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

    如果是这样,只使用 Rcpp 就足够了。

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

    如果是这样,您应该在 Armadillo 中编写算法的关键,并设置适当的 Hook 以使用 Rcpp 将函数导出到 R 中。

    Is there a performance/memory advantage of using one over the other?



    是的,如前所述,肯定有性能/内存优势。不仅如此,通过使用 RcppArmadillo,您可以有效地在 Rcpp 之上添加一个额外的库,从而增加整体安装占用空间、编译时间和系统要求(请参阅 macOS 构建的困境)。弄清楚您的项目需要什么,然后选择该结构。

    Are the only difference the member functions?



    不仅是成员函数,还有:
  • 矩阵分解方面的估计例程
  • 计算统计量值
  • 对象生成
  • 稀疏表示(避免操作 S4 对象)

  • 这些是 Rcpp 和 Armadillo 之间的根本区别。一个是为了促进将 R 对象转移到 C++ 中,而另一个是为了更严格的线性代数计算。正如 Rcpp 所做的那样,这应该很明显 不是 实现任何矩阵乘法逻辑,而 armadillo使用系统的基本线性代数子程序 (BLAS) 来执行计算。

    And, as a bonus question: should I even consider arma::colvec or arma::rowvec?



    取决于您希望如何返回结果。你想拥有一个: 1 x N (行向量)或 N x 1 (列向量)? RcppArmadillo默认情况下,将这些结构作为具有适当维度的矩阵对象而不是传统的一维 R 向量返回。

    举个例子:
    #include <RcppArmadillo.h>

    // [[Rcpp::depends(RcppArmadillo)]]

    // [[Rcpp::export]]
    arma::vec col_example(int n) {
    arma::vec x = arma::randu<arma::vec>(n);
    return x;
    }


    // [[Rcpp::export]]
    arma::rowvec row_example(int n) {
    arma::rowvec x = arma::randu<arma::rowvec>(n);
    return x;
    }

    测试:
    set.seed(1)
    col_example(4)
    # [,1]
    # [1,] 0.2655087
    # [2,] 0.3721239
    # [3,] 0.5728534
    # [4,] 0.9082078

    set.seed(1)
    row_example(4)
    # [,1] [,2] [,3] [,4]
    # [1,] 0.2655087 0.3721239 0.5728534 0.9082078

    关于r - 在 Rcpp 中决定 NumericVector 和 arma::vec,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50170166/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com