Rcpp函数比同样的R函数更低

use*_*870 4 c++ performance r rcpp

我一直在编写一个R函数来计算某些分布的积分,见下面的代码.

EVofPsi = function(psi, probabilityMeasure, eps=0.01, ...){

distFun = function(u){
 probabilityMeasure(u, ...)
}
xx = yy = seq(0,1,length=1/eps+1)
summand=0

for(i in 1:(length(xx)-1)){
  for(j in 1:(length(yy)-1)){
    signPlus = distFun(c(xx[i+1],yy[j+1]))+distFun(c(xx[i],yy[j]))
    signMinus = distFun(c(xx[i+1],yy[j]))+distFun(c(xx[i],yy[j+1]))
    summand = c(summand, psi(c(xx[i],yy[j]))*(signPlus-signMinus))
  }
}
sum(summand)
}
Run Code Online (Sandbox Code Playgroud)

它工作正常,但它很慢.通常会听到用C++等编译语言对函数进行重新编程会加快速度,特别是因为上面的R代码涉及双循环.我也是,使用Rcpp:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
double EVofPsiCPP(Function distFun, Function psi, int n, double eps) {

  NumericVector xx(n+1);
  NumericVector yy(n+1);
  xx[0] = 0;
  yy[0] = 0;

  // discretize [0,1]^2
  for(int i = 1; i < n+1; i++) {
      xx[i] = xx[i-1] + eps;
      yy[i] = yy[i-1] + eps;
  }

  Function psiCPP(psi);
  Function distFunCPP(distFun);
  double signPlus;
  double signMinus;
  double summand = 0;

  NumericVector topRight(2); 
  NumericVector bottomLeft(2);
  NumericVector bottomRight(2);
  NumericVector topLeft(2);

  // compute the integral
  for(int i=0; i<n; i++){
    //printf("i:%d \n",i);
    for(int j=0; j<n; j++){
      //printf("j:%d \n",j);
      topRight[0] = xx[i+1];
      topRight[1] = yy[j+1];
      bottomLeft[0] = xx[i];
      bottomLeft[1] = yy[j];
      bottomRight[0] = xx[i+1];
      bottomRight[1] = yy[j];
      topLeft[0] = xx[i];
      topLeft[1] = yy[j+1];
      signPlus = NumericVector(distFunCPP(topRight))[0] +  NumericVector(distFunCPP(bottomLeft))[0];
      signMinus = NumericVector(distFunCPP(bottomRight))[0] + NumericVector(distFunCPP(topLeft))[0];
      summand = summand + NumericVector(psiCPP(bottomLeft))[0]*(signPlus-signMinus);
      //printf("summand:%f \n",summand);
    }
  }
  return summand;
}
Run Code Online (Sandbox Code Playgroud)

我非常高兴,因为这个C++函数运行正常.但是,当我测试这两个函数时,C++运行了SLOWER:

sourceCpp("EVofPsiCPP.cpp")
pFGM = function(u,theta){
  u[1]*u[2] + theta*u[1]*u[2]*(1-u[1])*(1-u[2])
}
psi = function(u){
  u[1]*u[2]
}
print(system.time(
for(i in 1:10){
  test = EVofPsi(psi, pFGM, 1/100, 0.2)  
}
))
test

print(system.time(
  for(i in 1:10){
    test = EVofPsiCPP(psi, function(u){pFGM(u,0.2)}, 100, 1/100)  
  }
))
Run Code Online (Sandbox Code Playgroud)

那么,有什么样的专家愿意解释我这个吗?我是否像猴子一样编码,有没有办法加快这个功能?而且,我还有第二个问题.实际上,我可以用SEXP替换输出类型double,并且SEXP的参数类型也是函数,它似乎没有改变任何东西.那么区别是什么呢?

非常感谢Gildas

Rom*_*ois 11

其他人已在评论中回答.所以我只想强调一点:回调R函数很昂贵,因为我们需要对错误处理格外谨慎.只是在C++中使用循环并调用R函数不会在C++中重写代码.尝试重写psipFGM作为C++函数,并在此报告发生的情况.

您可能会认为您失去了一些灵活性,而您又无法使用任何R功能.对于这样的情况,我建议使用某种混合解决方案,其中您已经实现了C++中最常见的情况,否则回退到R解决方案.

至于另一个问题,a SEXP是一个R对象.这是R API的一部分.它可以是任何东西.当你Function从中创建一个(就像创建一个带Function参数的函数时隐式完成的那样),你可以保证这确实是一个R函数.开销非常小,但代码表现力的增加是巨大的.

  • +1 - 我们之前已经回答了类似的徒劳希望.如果在C++代码片段中只包含一个`R`函数使它更快,我们都会一直这样做.但是免费午餐定理仍然存在...... (7认同)