【问题标题】:Creating a simple Rcpp package with dependency with other Rcpp package创建一个依赖于其他 Rcpp 包的简单 Rcpp 包
【发布时间】:2019-10-20 08:20:08
【问题描述】:

我正在尝试通过使用foreach 来提高我的循环计算速度,但是我在这个循环中定义了一个简单的 Rcpp 函数。我将 Rcpp 函数保存为mproduct.cpp,然后调用该函数只需使用

sourceCpp("mproduct.cpp")

而Rcpp函数很简单,就是在C++中进行矩阵乘积:

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

#include <RcppArmadillo.h>
#include <RcppEigen.h>

// [[Rcpp::export]]
SEXP MP(const Eigen::Map<Eigen::MatrixXd> A, Eigen::Map<Eigen::MatrixXd> B){
  Eigen::MatrixXd C = A * B;
  return Rcpp::wrap(C);
}

所以,Rcpp 文件中的函数是MP,指的是矩阵乘积。我需要执行以下foreach 循环(我已简化代码以进行说明):

foreach(j=1:n, .package='Rcpp',.noexport= c("mproduct.cpp"),.combine=rbind)%dopar%{
n=1000000
A<-matrix(rnorm(n,1000,1000))
B<-matrix(rnorm(n,1000,1000))
S<-MP(A,B)
return(S)
} 

由于矩阵 A 和 B 的大小都很大,所以我想使用 foreach 来减轻计算成本。

但是,上面的代码不起作用,因为它向我提供了错误消息:

task 1 failed - "NULL value passed as symbol address"

我添加.noexport= c("mproduct.cpp") 的原因是听从解决类似问题的人的一些建议 (Can't run Rcpp function in foreach - "NULL value passed as symbol address")。但不知何故,这并不能解决我的问题。

所以我尝试将我的 Rcpp 函数安装为库。我使用了以下代码:

Rcpp.package.skeleton('mp',cpp_files = "<my working directory>")

但它会返回一条警告消息:

The following packages are referenced using Rcpp::depends attributes however are not listed in the Depends, Imports or LinkingTo fields of the package DESCRIPTION file: RcppArmadillo, RcppEigen 

所以当我尝试使用安装我的包时

install.packages("<my working directory>",repos = NULL,type='source')

我收到了警告信息:

Error in untar2(tarfile, files, list, exdir, restore_times) : 
  incomplete block on file
In R CMD INSTALL
Warning in install.packages :
  installation of package ‘C:/Users/Lenovo/Documents/mproduct.cpp’ had non-zero exit status

那么有人可以帮我解决如何解决 1) 使用带有 Rcpp 函数 MPforeach,或者 2) 将 Rcpp 文件安装为一个包?

非常感谢大家。

【问题讨论】:

  • Rcpp 代码是玩具示例还是您的真实代码?我怀疑您是否可以通过使用 RcppEigen(或 RcppArmadillo)获得比 %*% 更多的收益。但是,当您需要使用其中一个包时,您应该决定要使用哪一个(您当前的代码根本不使用 Armadillo ...)。之后,使用那个包中的*.package.skeleton()函数。
  • 欢迎来到 StackOverflow,Rico。 Ralf 是正确的:其他包也有你应该使用的骨架生成器。
  • @RalfStubner 感谢您的评论。 Rcpp 代码是我的真实代码,foreach 代码是我的玩具代码,说明当 Rcpp 函数位于内部时,我正在尝试执行 foreach 循环。很抱歉,但仍然没有得到你的两点:1)我应该如何使用 RcppEigen 改进Eigen::MatrixXd C = A * B?和 2) 我应该如何修改我的 Rcpp 代码以便我只能使用 RcppEigen 包?
  • @DirkEddelbuettel 谢谢。但我对 Rcpp 很陌生,我需要做的就是使矩阵乘积 AB 尽可能快。我不确定哪些包有骨架生成器。
  • Rico:请阅读(至少)Rcpp Introduction 小插图;它会清除很多事情。它还提到了一点 RcppArmadillo。 Ralf 和我指出了该包中的函数RcppArmadillo.package.skeleton()(或在 RcppEigen 中对称)。最后,我们试图向您解释 R 已经使用了相同的 LAPACK 矩阵乘法代码。仅通过从 C++ 调用它,您将获得很少甚至一无所获——但您可以通过一些基准测试自己看看。

标签: foreach rcpp r-package


【解决方案1】:

第一步是确保您正在优化正确的内容。对我来说,不会像这个简单的基准测试显示的那样:

set.seed(42)
n <- 1000
A<-matrix(rnorm(n*n), n, n)
B<-matrix(rnorm(n*n), n, n)

MP <- Rcpp::cppFunction("SEXP MP(const Eigen::Map<Eigen::MatrixXd> A, Eigen::Map<Eigen::MatrixXd> B){
  Eigen::MatrixXd C = A * B;
  return Rcpp::wrap(C);
}", depends = "RcppEigen")

bench::mark(MP(A, B), A %*% B)[1:5]
#> # A tibble: 2 x 5
#>   expression      min   median `itr/sec` mem_alloc
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>
#> 1 MP(A, B)    277.8ms    278ms      3.60    7.63MB
#> 2 A %*% B      37.4ms     39ms     22.8     7.63MB

所以对我来说,通过%*% 的矩阵乘积比通过 RcppEigen 的矩阵乘积快几倍。但是,当您在 Windows 上时,我使用 Linux 和 OpenBLAS 进行矩阵运算,这通常意味着参考 BLAS 进行矩阵运算。 可能 RcppEigen 在您的系统上更快。我不确定 Windows 用户获得更快的 BLAS 实现有多困难(https://csgillespie.github.io/efficientR/set-up.html#blas-and-alternative-r-interpreters 可能包含一些指针),但我建议花一些时间对此进行调查。

现在,如果您得出结论,您的代码中确实需要 RcppEigen 或 RcppArmadillo,并且想要将该代码放入一个包中,您可以执行以下操作。而不是Rcpp::Rcpp.package.skeleton() 使用RcppEigen::RcppEigen.package.skeleton() RcppArmadillo::RcppArmadillo.package.skeleton() 分别为基于RcppEigen RcppArmadillo 的包创建起点。

【讨论】:

  • 不错的答案。虽然我也不使用 Windows,但它在 R 附带的 R 手册之一的附录中有详细说明,可能是 R 安装和管理 - 多年来,Ripley 教授通过从他的网站下载自定义下载来支持这一点。
  • @Ralf Stubner 感谢您提供这些信息。这很奇怪,因为我使用与您相同的代码,MP(A,B) 有 21.33445 分钟,A%*%B 有 143.46149 分钟。可能是由于操作系统的不同。我会仔细阅读你的帖子并修改我的文件。欣赏!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-25
  • 2013-01-26
  • 2013-12-26
  • 1970-01-01
  • 2021-12-06
相关资源
最近更新 更多