代码之家  ›  专栏  ›  技术社区  ›  ecogrammer

简单Rcpp函数中的内存泄漏

  •  5
  • ecogrammer  · 技术社区  · 7 年前

    我正在开发一个R包,我想将其转换为Rcpp以获得更好的性能。我对Rcpp(和一般的C++)是新手我的问题是,如果我用一组参数多次运行我编写的Rcpp函数,它会工作得很好,但如果我尝试在多个参数组合上循环它,它会导致内存泄漏并导致R会话中止。

    下面是R中的代码,它很好地支持我对它进行的任何测试:

    raw_noise <- function(timesteps, mu, sigma, phi) {
       delta <- mu * (1 - phi)
       variance <- sigma^2 * (1 - phi^2)
       noise <- vector(mode = "double", length = timesteps)
       noise[1] <- c(rnorm(1, mu, sigma))
       for (i in (1:(timesteps - 1))) {
         noise[i + 1] <- delta + phi * noise[i] + rnorm(1, 0, sqrt(variance))
      }
       return(noise)
    }
    

    下面是Rcpp中的代码,使用三个 Rcpp sugar functions (pow、sqrt、rnorm):

    NumericVector raw_noise(int timesteps, double mu, double sigma, double phi) {
      double delta = mu * (1 - phi);
      double variance = pow(sigma, 2.0) * (1 - pow(phi, 2.0));
      NumericVector noise(timesteps);
      noise[0] = R::rnorm(mu, sigma);
      for(int i = 0; i < timesteps; ++i) {
        noise[i+1] = delta + phi*noise[i] + R::rnorm(0, sqrt(variance));
      }
      return noise;
    }
    

    真正让我困惑的是,这段代码运行时没有出现问题:

    library(purrr)
    rerun(10000, raw_noise(timesteps = 30, mu = 0.5, sigma = 0.2, phi = 0.3))
    

    但当我运行此代码时:

    test_loop <- function(timesteps, mu, sigma, phi, replicates) {
      params <- cross_df(list(timesteps = timesteps, phi = phi, mu = mu, sigma = 
    sigma))
      for (i in 1:nrow(params)) {
        print(params[i,])
        pmap(params[i,], raw_noise)
      }
    }
    library(purrr)
    test_loop(timesteps=c(5, 6, 7, 8, 9, 10), mu=c(0.2, 0.5), sigma=c(0.2, 0.5),
          phi=c(0, 0.1))
    

    R会话通常会中止,RStudio也会完全崩溃。但有时我会在R会话中止之前捕获此错误消息:

    匹配中出错(x,table,nomatch=0L):GC遇到一个节点 (0x10db7af50),内存中存在未知的SEXP类型:NEWSXP。c: 1692年

    据我所知,NEWSXP是R中的一种奇特的对象类型,很少出现。在我看来,发生的事情就像是内存泄漏,但我根本不知道如何修复它。正如我所说,我对Rcpp和C++一般来说都是新手,所以我希望能朝着正确的方向前进。

    1 回复  |  直到 7 年前
        1
  •  6
  •   coatless    7 年前

    您有一个越界错误:

    for(int i = 0; i < timesteps; ++i)
    

    原因

    noise[i+1]
    

    超出定义的范围 C++ 索引从0开始,而不是从1开始。


    例如 0 timesteps - 1 长度为 timesteps 因此,这是可以的。

    但是

    0 时间步长 长度为 timesteps + 1

    如果您更改,可以看到这一点 noise[i+1] noise(i+1) ,它对请求的索引执行边界检查。

    Error in raw_noise(100, 2, 3, 0.2) : 
      Index out of bounds: [index=100; extent=100].
    

    要解决此问题,请进行以下更改:

    NumericVector raw_noise(int timesteps, double mu, double sigma, double phi) {
      double delta = mu * (1 - phi);
      double variance = pow(sigma, 2.0) * (1 - pow(phi, 2.0));
      NumericVector noise(timesteps);
      noise[0] = R::rnorm(mu, sigma);
    
      // change here
      for(int i = 0; i < timesteps - 1; ++i) { // 1 less time step
    
        noise[i+1] = delta + phi*noise[i] + R::rnorm(0, sqrt(variance));
      }
      return noise;
    }