首页 > 解决方案 > 创建一个依赖于其他 Rcpp 包的简单 Rcpp 包

问题描述

我试图通过使用来提高我的循环计算速度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")是遵循解决类似问题的人的一些建议(无法在 foreach 中运行 Rcpp 函数 - “NULL 值作为符号地址传递”)。但不知何故,这并不能解决我的问题。

所以我尝试将我的 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)使用foreachRcpp函数MP,还是2)将Rcpp文件安装为一个包?

非常感谢大家。

标签: foreachrcppr-package

解决方案


第一步是确保您正在优化正确的东西。对我来说,情况并非如此,因为这个简单的基准测试显示:

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() or RcppArmadillo::RcppArmadillo.package.skeleton()来创建基于RcppEigen or RcppArmadillo的包的起点。


推荐阅读