我怎么能加快这个Rcpp代码?

时间:2014-07-16 12:58:18

标签: r rcpp

我在R中实现了一个很长时间运行的函数。我已经成功地在R中进行了改进,但现在我想通过使用Rcpp包加快速度。

我创建了以下Rcpp代码。不幸的是,运行R代码大约需要相同的时间。我想这样改进它。有没有人知道如何改进这段代码?

非常感谢!

#include <math.h>
#include <Rcpp.h>
using namespace Rcpp;


// [[Rcpp::export]]
double kernelcpp(NumericVector a, NumericVector b, int N){
  int i;
  double sum=0.0;
  for (i=0;i<N;i++){
    if (a[i] > b[i])
      sum+= a[i] - b[i];
    else
      sum+= b[i] - a[i];
  }
  return(exp( - sum));
}
// [[Rcpp::export]]
NumericVector testFromontcpp(NumericMatrix z1, NumericMatrix z2, int Nbootstrap){
  // first element of TKeps = TK
  int i,j,k,t;
  int dim1 = z1.nrow();
  int dim2 = z2.nrow();
  double n1 = (double) dim1;
  double n2 = (double) dim2;
  int dimension = z1.ncol();
  int N = dim1 + dim2;
  NumericVector TKeps(Nbootstrap+1);
  Rcpp::NumericMatrix bb(N,N);

  double cc = 1 / (n1*n2*(n1+n2-2));
  double a = sqrt(1/(n1*n1-n1)-cc);
  double b = - sqrt(1/(n2*n2-n2)-cc);

  for (i=0 ; i<N ; i++){
    for (j=0 ; j<N ; j++){
    if (i != j){
      if (i < dim1) {
        if (j < dim1){
          bb(i,j) = kernelcpp(z1(i,_),z1(j,_),dimension);
        } else {
          bb(i,j) = kernelcpp(z1(i,_),z2(j-dim1,_),dimension);
        }
      }
      else{
        if (j < dim1){
          bb(i,j) = kernelcpp(z2(i-dim1,_),z1(j,_),dimension);
        } else {
          bb(i,j) = kernelcpp(z2(i-dim1,_),z2(j-dim1,_),dimension);
        }
      }
    }
    }
  }

  TKeps(0)=0.0;
  for (i=0 ; i<N ; i++){
    for (j=0 ; j<N ; j++){
    if (i != j){
      if (i < dim1) {
        if (j < dim1){
          TKeps(0) += bb(i,j)* (a*a + cc);
        } else {
          TKeps(0) += bb(i,j) * (a*b + cc);
        }
      }
      else{
        if (j < dim1){
          TKeps(0) += bb(i,j) * (a*b + cc);
        } else {
          TKeps(0) += bb(i,j) * (b*b + cc);
        }
      }
    }
    }
  }


  for (k=1 ; k<=Nbootstrap ; k++){
    TKeps(k)=0;
    int R[N];
    for (i = 0 ; i < N ; i++)
      R[i] = i;
    for (i = 0; i < N - 1 ; i++) {
      int j = i + rand() / (RAND_MAX / (N - i) + 1);
      t = R[j];
      R[j] = R[i];
      R[i] = t;
    }

    for (i=0 ; i<N ; i++){
      for (j=0 ; j<N ; j++){
        if (i != j){ 
          if (R[i] < n1) {
            if (R[j] < n1){
              TKeps(k) += bb(i,j) * (a*a + cc);
            } else {
              TKeps(k) += bb(i,j) * (a*b + cc);
            }
          } else{
            if (R[j] < n1){
              TKeps(k) += bb(i,j) * (b*a + cc);
            } else {
              TKeps(k) += bb(i,j) * (b*b + cc);
            }
          }
        }
      }
    }
  }
  return(TKeps);
}

1 个答案:

答案 0 :(得分:4)

由于我不确切知道你的代码是做什么的,所以我从头开始可以看到两件事:

  • 您从R环境调用的函数是testFromontcpp(...)。我建议这个函数应该有SEXP值作为参数。那些S-Expressions指向R的内存。如果你不使用SEXP,那么两个矩阵都将被复制: 考虑1000x1000矩阵,这意味着您在R中保存了100万个条目,这些条目被复制到C ++。为此,请写下:

    testFromontcpp(SEXP x,SEXP y,SEXP z){

    NumericMatrix z1(x),z2(y);

    int * Nbootstrap = INTEGER(z);

    ... }

注意:在for循环中,您无法使用i<Nbootstrap。你必须写i<*Nbootstrap !!!

  • 其次......更重要的是:由于R的矩阵被保存为指向列的指针,而从列指针保存到行,因此C的矩阵被反过来保存。我想说的是,跳入内存并跳回整个时间而不是遵循内存路径需要花费很多。我的建议是:切换for循环,所以首先遍历特定列的行,而不是反过来。

到最后一点:在大学的一项任务中,我也遇到了迭代矩阵的问题。在我的情况下,转换矩阵然后进行计算会更便宜。

我希望我能帮到你。

最佳, 迈克尔

PS:参考第1点......我只是使用您的实现和使用SEXP对您的代码进行基准测试。使用SEXP,对于100x100矩阵,随机数在1到10之间会略快一些。