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

gag*_*ews 8 r exp rcpp

好的,我知道答案,但受到这个问题的启发,我想对以下内容得到一些好的看法:为什么下面的Rcpp练习是ca. 比内置的快15%(对于长向量)exp()?我们都知道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
Run Code Online (Sandbox Code Playgroud)

Dir*_*tel 8

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

所以我补充道

Rcpp::cppFunction("NumericVector expSugar(NumericVector x) { return exp(x); }")
Run Code Online (Sandbox Code Playgroud)

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

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> 
Run Code Online (Sandbox Code Playgroud)

  • @Roland:为什么不在https://github.com/wch/r-source/blob/trunk/src/main/arithmetic.c#L1156上阅读来源,而不是和我争论?基本R函数执行更多的arg检查,有效性检查,这个,以及OP的五行功能不执行的另一个. (3认同)

Rom*_*ois 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))
*/
Run Code Online (Sandbox Code Playgroud)

随着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
Run Code Online (Sandbox Code Playgroud)

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

有了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
Run Code Online (Sandbox Code Playgroud)