为什么自编的Rcpp矢量化数学函数比其基础对应物更快?

时间:2014-10-17 18:21:24

标签: r rcpp exp

好的,我知道答案,但受到this question的启发,我想对以下内容有一些很好的看法:为什么下面的Rcpp练习是ca.比内置exp()快15%(对于长向量)?我们都知道Rcpp是R / C API的包装器,所以我们应该期待性能稍差。

Rcpp::cppFunction("
   NumericVector exp2(NumericVector x) {
      NumericVector z = Rcpp::clone(x);
      int n = z.size();
      for (int i=0; i<n; ++i)
         z[i] = exp(z[i]);
      return z;
   }
")

library("microbenchmark")
x <- rcauchy(1000000)
microbenchmark(exp(x), exp2(x), unit="relative")
## Unit: relative
##     expr      min       lq   median       uq      max neval
##   exp(x) 1.159893 1.154143 1.155856 1.154482 0.926272   100
##  exp2(x) 1.000000 1.000000 1.000000 1.000000 1.000000   100

2 个答案:

答案 0 :(得分:8)

Base R倾向于对NA进行更多检查,因此我们可以通过不做那样来赢得一点。另请注意,通过像循环展开这样的技巧(如在Rcpp Sugar中所做的那样),我们可以做得更好。

所以我添加了

Rcpp::cppFunction("NumericVector expSugar(NumericVector x) { return exp(x); }")

并且我得到了进一步的收益 - 用户端的代码更少:

R> microbenchmark(exp(x), exp2(x), expSugar(x), unit="relative")
Unit: relative
        expr     min      lq    mean  median      uq     max neval
      exp(x) 1.11190 1.11130 1.11718 1.10799 1.08938 1.02590   100
     exp2(x) 1.08184 1.08937 1.07289 1.07621 1.06382 1.00462   100
 expSugar(x) 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000   100
R> 

答案 1 :(得分:5)

如果您确实希望获得性能改进,则必须编写代码以利用底层硬件并发性。您可以使用RcppParallel包执行此操作,其parallelFor将是理想的容器。

您还可以尝试R/C++的更现代的实现。几天内发布的Rcpp11的下一个版本将自动添加螺纹糖,使前一个答案中的expSugar更好。

考虑:

#include <Rcpp.h>
using namespace Rcpp ;

// [[Rcpp::export]]
NumericVector exp2(NumericVector x) {
   NumericVector z = Rcpp::clone(x);
   int n = z.size();
   for (int i=0; i<n; ++i)
      z[i] = exp(z[i]);
   return z;
}

// [[Rcpp::export]]
NumericVector expSugar(NumericVector x) {
    return exp(x) ;
}

/*** R
    library(microbenchmark)
    x <- rcauchy(1000000)
    microbenchmark(exp(x), exp2(x), expSugar(x))
*/

Rcpp我得到:

$ RcppScript /tmp/exp.cpp

> library(microbenchmark)

> x <- rcauchy(1e+06)

> microbenchmark(exp(x), exp2(x), expSugar(x))
Unit: milliseconds
        expr      min       lq   median       uq      max neval
      exp(x) 7.027006 7.222141 7.421041 8.631589 21.78305   100
     exp2(x) 6.631870 6.790418 7.064199 8.145561 31.68552   100
 expSugar(x) 6.491868 6.761909 6.888111 8.154433 27.36302   100

如此好,但有点轶事改进,可以通过各种内联等解释......如其他答案和评论中所述。

使用Rcpp11和自动螺纹糖,我得到:

$ Rcpp11Script /tmp/exp.cpp

> library(microbenchmark)

> x <- rcauchy(1e+06)

> microbenchmark(exp(x), exp2(x), expSugar(x))
Unit: milliseconds
        expr      min       lq   median       uq      max neval
      exp(x) 7.029882 7.077804 7.336214 7.656472 15.38953   100
     exp2(x) 6.636234 6.748058 6.917803 7.017314 12.09187   100
 expSugar(x) 1.652322 1.780998 1.962946 2.261093 12.91682   100