简单 Rcpp 函数中的内存泄漏
Memory leaks in a simple Rcpp function
我正在用 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 会话中止之前捕获此错误消息:
Error in match(x, table, nomatch = 0L) : GC encountered a node
(0x10db7af50) with an unknown SEXP type: NEWSXP at memory.c:1692
据我了解,NEWSXP 是 R 中不常出现的奇异对象类型。发生的事情在我看来像是内存泄漏,但我完全不确定如何修复它。就像我说的,我通常是 Rcpp 和 C++ 的新手,所以我很感激任何正确方向的推动。
你有一个越界错误:
for(int i = 0; i < timesteps; ++i)
原因
noise[i+1]
超出定义的范围,因为 C++ 索引从 0 而不是 1 开始。
例如,0
到 timesteps - 1
的长度为 timesteps
,因此可以。
但是
0
到 timesteps
的长度为 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;
}
我正在用 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 会话中止之前捕获此错误消息:
Error in match(x, table, nomatch = 0L) : GC encountered a node (0x10db7af50) with an unknown SEXP type: NEWSXP at memory.c:1692
据我了解,NEWSXP 是 R 中不常出现的奇异对象类型。发生的事情在我看来像是内存泄漏,但我完全不确定如何修复它。就像我说的,我通常是 Rcpp 和 C++ 的新手,所以我很感激任何正确方向的推动。
你有一个越界错误:
for(int i = 0; i < timesteps; ++i)
原因
noise[i+1]
超出定义的范围,因为 C++ 索引从 0 而不是 1 开始。
例如,0
到 timesteps - 1
的长度为 timesteps
,因此可以。
但是
0
到 timesteps
的长度为 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;
}