gpt4 book ai didi

r - 为什么在我的示例中Rcpp实现比R函数慢得多?

转载 作者:行者123 更新时间:2023-12-04 13:56:25 25 4
gpt4 key购买 nike

我有一些C++和R的经验,但是是Rcpp的新手。最近,我在以前的一些项目中使用Rcpp取得了巨大的成功,因此决定将其应用于新项目。我很惊讶我的Rcpp代码可能比相应的R函数慢得多。我试图简化我的R函数以找出原因,但找不到任何线索。非常欢迎您的帮助和评论!

比较R和Rcpp实现的主要R函数:

main <- function(){

n <- 50000
Delta <- exp(rnorm(n))
delta <- exp(matrix(rnorm(n * 5), nrow = n))
rx <- matrix(rnorm(n * 20), nrow = n)
print(microbenchmark(c1 <- test(Delta, delta, rx), times = 500))
print(microbenchmark(c2 <- rcpp_test(Delta, delta, rx), times = 500))

identical(c1, c2)
list(c1 = c1, c2 = c2)
}

R的实现:
test <- function(Delta, delta, rx){

const <- list()
for(i in 1:ncol(delta)){
const[[i]] <- rx * (Delta / (1 + delta[, i]))
}

const

}

rcpp的实现:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
List rcpp_test(NumericVector Delta,
NumericMatrix delta,
NumericMatrix rx) {

int n = Delta.length();
int m = rx.ncol();

List c;
NumericMatrix c1;
for(int i = 0; i < delta.ncol(); ++i){
c1 = NumericMatrix(n, m);
for(int k = 0; k < n; ++k){
double tmp = Delta[k] / (1 + delta(k, i));
for(int j = 0; j < c1.ncol(); ++j){
c1(k, j) = rx(k, j) * tmp;
}
}
c.push_back(c1);
}

return c;

}

我知道不能保证使用Rcpp可以提高效率,但是鉴于我在此处显示的简单示例,我不明白为什么Rcpp代码运行得如此缓慢。
Unit: milliseconds
expr min lq mean median uq max neval
c1 <- test(Delta, delta, rx) 13.16935 14.19951 44.08641 30.43126 73.78581 115.9645 500
Unit: milliseconds
expr min lq mean median uq max neval
c2 <- rcpp_test(Delta, delta, rx) 143.1917 158.7481 171.6116 163.413 173.7677 247.5495 500

理想情况下, rx是我的项目中的矩阵列表。 for循环中的 i变量将用于选择要计算的元素。在开始时,我怀疑将 List传递给Rcpp会产生高开销,因此在此示例中,我假设 rx是用于所有 i的固定矩阵。看来这不是缓慢的原因。

最佳答案

您的R代码似乎或多或少是最优的,即所有实际工作都是在编译后的代码中完成的。对于C++代码,我可以找到的主要问题是在紧密循环中调用c1.ncol()。如果将其替换为m,则C++解决方案的速度几乎与R一样快。如果将RcppArmadillo添加到混合中,我将获得非常紧凑的语法,但不会比纯Rcpp代码快。对我来说,这表明很难打败编写良好的R代码:

//  [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
using namespace Rcpp;

// [[Rcpp::export]]
List arma_test(const arma::vec& Delta,
const arma::mat& delta,
const arma::mat& rx) {
int l = delta.n_cols;
List c(l);

for (int i = 0; i < l; ++i) {
c(i) = rx.each_col() % (Delta / (1 + delta.col(i)));
}

return c;
}

// [[Rcpp::export]]
List rcpp_test(NumericVector Delta,
NumericMatrix delta,
NumericMatrix rx) {

int n = Delta.length();
int m = rx.ncol();

List c(delta.ncol());
NumericMatrix c1;
for(int i = 0; i < delta.ncol(); ++i){
c1 = NumericMatrix(n, m);
for(int k = 0; k < n; ++k){
double tmp = Delta[k] / (1 + delta(k, i));
for(int j = 0; j < m; ++j){
c1(k, j) = rx(k, j) * tmp;
}
}
c(i) = c1;
}

return c;

}

/*** R
test <- function(Delta, delta, rx){

const <- list()
for(i in 1:ncol(delta)){
const[[i]] <- rx * (Delta / (1 + delta[, i]))
}

const

}

n <- 50000
Delta <- exp(rnorm(n))
delta <- exp(matrix(rnorm(n * 5), nrow = n))
rx <- matrix(rnorm(n * 20), nrow = n)
bench::mark(test(Delta, delta, rx),
arma_test(Delta, delta, rx),
rcpp_test(Delta, delta, rx))
*/

输出:
# A tibble: 3 x 14
expression min mean median max `itr/sec` mem_alloc n_gc n_itr
<chr> <bch:t> <bch:t> <bch:t> <bch:t> <dbl> <bch:byt> <dbl> <int>
1 test(Delt… 84.3ms 85.2ms 84.9ms 86.6ms 11.7 44.9MB 2 4
2 arma_test… 106.5ms 107.7ms 107.7ms 108.9ms 9.28 38.1MB 3 2
3 rcpp_test… 101.9ms 103.2ms 102.2ms 106.6ms 9.69 38.1MB 1 4
# … with 5 more variables: total_time <bch:tm>, result <list>, memory <list>,
# time <list>, gc <list>

我还明确地将输出列表初始化为所需的大小,避免了 push_back,但这并没有太大的区别。使用Rcpp中的向量一样的数据结构时,您绝对应该避免使用 push_back,因为每次扩展向量时都会创建一个副本。

关于r - 为什么在我的示例中Rcpp实现比R函数慢得多?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55555754/

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