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

时间:2013-07-30 21:55:43

标签: c++ r performance 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)
}

它工作正常,但速度很慢。通常会听到用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;
}

我很高兴,因为这个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)  
  }
))

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

非常感谢您提前, 吉尔达斯

1 个答案:

答案 0 :(得分:10)

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

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

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