加快dnorm功能

时间:2014-12-11 14:51:34

标签: r performance

我有一些R-Script,这需要花费太多时间。在分析之后,我意识到函数dnorm花费了大部分时间。 dnorm的输入是大小为11000 x 11000的矩阵。相应地,输出也是相同大小的矩阵。您怎么看?如何才能加快dnorm的速度?任何想法都是受欢迎的,将受到高度赞赏。非常感谢提前!

1 个答案:

答案 0 :(得分:4)

就像@duffymo所说的那样,你有一个O(n ^ 2)问题,所以不会有任何戏剧性的加速。我唯一能想到的就是自己明确地计算dnorm。这似乎导致我的机器上的温和改善约40%。例如,如果您的正态分布参数为(0,1),则:

> x<-rnorm(10^7)
> system.time(dx<-dnorm(x))
   user  system elapsed 
  0.945   0.004   0.949 
> system.time(dx0<-exp(-x^2/2)/sqrt(2*pi))
   user  system elapsed 
  0.559   0.000   0.560 
> max(abs(dx0-dx))
[1] 5.551115e-17

不确定为什么会发生这种情况。如果你调用dnorm,R可能会为向量中的每个值单独计算平方根。

编辑时:这里有100个样本的时间:

> microbenchmark(dx0<-exp(-x^2/2)/sqrt(2*pi),dx<-dnorm(x))
Unit: milliseconds
                            expr      min       lq     mean   median        uq
 dx0 <- exp(-x^2/2)/sqrt(2 * pi) 481.5091 508.1434 522.1289 511.9739  519.9924
                  dx <- dnorm(x) 944.4744 991.4800 998.2794 995.0194 1004.2889
       max neval
  752.3929   100
 1149.2694   100

再次编辑。您可以做的另一件事是并行计算dnorm。您可以使用Rcpp直接从R执行此操作(假设为(0,1) - 您可以轻松地针对其他参数调整此值):

require(Rcpp)
Sys.setenv("PKG_CXXFLAGS"="-fopenmp")
Sys.setenv("PKG_LIBS"="-fopenmp")
sourceCpp(code = '#include <Rcpp.h>
  #include <omp.h>
  using namespace Rcpp;
  // [[Rcpp::export]]
  NumericVector dnormpar(NumericVector x){
  double c = 1/sqrt(2*PI);  
  int n = x.size();
  NumericVector ret(n);
  #pragma omp parallel for if(n> 50000)
  for(int i=0; i<n; ++i)
    ret[i] = exp(-x[i]*x[i]/2)*c;
  return ret;
}')

您获得的改进程度取决于您拥有的核心数量。我比第一个建议提高了8倍:

> dx1<-dnormpar(x)
> max(abs(dx1-dx))
[1] 8.470329e-22
> microbenchmark(dx1<-dnormpar(x))
Unit: milliseconds
               expr     min       lq     mean   median       uq      max neval
 dx1 <- dnormpar(x) 39.9888 65.60082 74.03912 68.81251 71.35201 226.9584   100

最终编辑。这是一个同时采用均值和标准偏差的版本,如果R短于x,R的行为会重复地在mu或sigma上循环。

require(Rcpp)
sourceCpp(code = '#include <Rcpp.h>
  #include <omp.h>
  using namespace Rcpp;
  // [[Rcpp::export]]
  NumericVector dnormpar2(NumericVector x, NumericVector mu, NumericVector sig){
  double c = 1/sqrt(2*PI);  
  int n = x.size();
  int muSize = mu.size();
  int sigSize = sig.size();
  NumericVector ret(n);
  double x0,s0;
  #pragma omp parallel for if(n> 50000) private(x0,s0)
  for(int i=0; i<n; ++i){
    s0 = sig[i % sigSize];
    x0 = x[i]-mu[i % muSize];
    ret[i] = exp(-x0*x0/(2*s0*s0))*c/s0;
  }
  return ret;
}')

在R:

> mu<-rnorm(length(x))
> sig<-runif(length(x))
> dx<-dnorm(x,mu,sig)
> dx2<-dnormpar2(x,mu,sig)
> max(abs(dx-dx2))
[1] 1.136868e-13