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