减少 R 中的内存消耗——通过引用传递 / data.table
Decreasing memory consumption in R -- pass by reference / data.table
通过将子集操作从基本 data.frame
操作移动到 data.table
操作,我已经实现了显着的加速(~6.5 倍)。但我想知道我是否可以在记忆力方面得到任何改善。
我的理解是 R 不是 本机传递引用(例如 see here)。所以,我正在寻找一种方法(除了在 Rcpp
中重写一个复杂的函数)来做到这一点。 data.table
提供了一些改进 [ 在编辑我的问题以包括下面由 @joshua ulrich 发现的拼写错误后 ]。但如果可能的话,我正在寻找更大的改进。
- 另一个选项可能是 R.oo package, though I haven't yet found a good tutorial. (I still need to read this.
- reference classes会有帮助吗?
在我的实际用例中,我正在对大量数据集进行并行模拟,并通过模拟退火进行优化。由于增加的开发时间和增加的 technical debt.
,我不想在 Rcpp 中重写模拟退火和我的损失函数计算
问题示例:
我主要关心的是从数据集中删除一些观察子集并添加另一个观察子集。这里给出了一个非常简单(无意义)的例子。有没有办法减少内存使用?我当前的使用情况似乎是按值传递,因此内存使用量 (RAM) 大约翻了一番。
library(data.table)
set.seed(444L)
df1 <- data.frame(matrix(rnorm(1e7), ncol= 10))
df2 <- data.table(matrix(rnorm(1e7), ncol= 10))
prof_func <- function(df) {
s1 <- sample(1:nrow(df), size= 500, replace=F)
s2 <- sample(1:nrow(df), size= 500, replace=F)
return(rbind(df[-s1,], df[s2,]))
}
dt_m <- df_m <- vector("numeric", length= 500L)
for (i in 1:500) {
Rprof("./DF_mem.out", memory.profiling = TRUE)
y <- prof_func(df1)
Rprof(NULL)
df <- summaryRprof("./DF_mem.out", memory= "both")
df_m[i] <- df$by.self$mem.total[which(rownames(df$by.self) == "\"rbind\"")]
Rprof("./DT_mem.out", memory.profiling = TRUE)
y2 <- prof_func(df2)
Rprof(NULL)
dt <- summaryRprof("./DT_mem.out", memory = "both")
dt_m[i] <- dt$by.self$mem.total[which(rownames(dt$by.self) == "\"rbind\"")]
}
pryr::object_size(df1)
80 MB
pryr::object_size(df2)
80 MB
# EDITED: via typo / fix from @Joshua Ulrich.
# improvement in memory usage via DT. still not pass-by-reference
quantile(df_m, seq(0,1,.1))
0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
379.00 428.60 440.10 447.70 455.36 459.20 466.48 469.89 474.40 482.10 512.60
quantile(dt_m, seq(0,1,.1))
0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
76.80 84.50 84.50 92.10 92.10 92.10 92.10 107.30 116.46 130.20 157.00
附录:
### speed improvement:
#-----------------------------------------------
library(data.table)
library(microbenchmark)
set.seed(444L)
df1 <- data.frame(matrix(rnorm(1e7), ncol= 10))
df2 <- data.table(matrix(rnorm(1e7), ncol= 10))
microbenchmark(
df= {
s1 <- sample(1:nrow(df1), size= 500, replace=F)
s2 <- sample(1:nrow(df1), size= 500, replace=F)
df1 <- rbind(df1[-s1,], df1[s2,])
},
dt= {
s1 <- sample(1:nrow(df2), size= 500, replace=F)
s2 <- sample(1:nrow(df2), size= 500, replace=F)
df2 <- rbind(df2[-s1,], df2[s2,])
}, times= 100L)
Unit: milliseconds
expr min lq mean median uq max neval cld
df 672.5106 757.65188 814.1582 809.6346 864.6668 998.2290 100 b
dt 68.1254 85.73178 139.1256 120.3613 148.8243 397.7359 100 a
prof_func
有错误。它在 df1
上调用 rbind
而不是它的参数 (df
)。解决这个问题,您将看到 data.table 对象的内存使用量减少。
library(data.table)
set.seed(444L)
df1 <- data.frame(matrix(rnorm(1e7), ncol= 10))
df2 <- data.table(matrix(rnorm(1e7), ncol= 10))
prof_func <- function(df) {
s1 <- sample(1:nrow(df), size= 500, replace=F)
s2 <- sample(1:nrow(df), size= 500, replace=F)
return(rbind(df[-s1,], df[s2,]))
}
dt_m <- df_m <- vector("numeric", length= 500L)
for (i in 1:100) {
Rprof("./DF_mem.out", memory.profiling = TRUE, interval=0.01)
y <- prof_func(df1)
Rprof(NULL)
df <- summaryRprof("./DF_mem.out", memory= "both")
df_m[i] <- df$by.total["\"rbind\"","mem.total"]
Rprof("./DT_mem.out", memory.profiling = TRUE, interval=0.01)
y2 <- prof_func(df2)
Rprof(NULL)
dt <- summaryRprof("./DT_mem.out", memory = "both")
dt_m[i] <- dt$by.total["\"rbind\"","mem.total"]
}
quantile(df_m, seq(0,1,.1))
# 0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
# 0.0 0.0 0.0 0.0 0.0 0.0 0.0 413.4 432.5 455.0 485.9
quantile(dt_m, seq(0,1,.1))
# 0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
# 0.0 0.0 0.0 0.0 0.0 0.0 0.0 53.9 84.5 122.6 153.1
通过将子集操作从基本 data.frame
操作移动到 data.table
操作,我已经实现了显着的加速(~6.5 倍)。但我想知道我是否可以在记忆力方面得到任何改善。
我的理解是 R 不是 本机传递引用(例如 see here)。所以,我正在寻找一种方法(除了在 Rcpp
中重写一个复杂的函数)来做到这一点。 data.table
提供了一些改进 [ 在编辑我的问题以包括下面由 @joshua ulrich 发现的拼写错误后 ]。但如果可能的话,我正在寻找更大的改进。
- 另一个选项可能是 R.oo package, though I haven't yet found a good tutorial. (I still need to read this.
- reference classes会有帮助吗?
在我的实际用例中,我正在对大量数据集进行并行模拟,并通过模拟退火进行优化。由于增加的开发时间和增加的 technical debt.
,我不想在 Rcpp 中重写模拟退火和我的损失函数计算问题示例:
我主要关心的是从数据集中删除一些观察子集并添加另一个观察子集。这里给出了一个非常简单(无意义)的例子。有没有办法减少内存使用?我当前的使用情况似乎是按值传递,因此内存使用量 (RAM) 大约翻了一番。
library(data.table)
set.seed(444L)
df1 <- data.frame(matrix(rnorm(1e7), ncol= 10))
df2 <- data.table(matrix(rnorm(1e7), ncol= 10))
prof_func <- function(df) {
s1 <- sample(1:nrow(df), size= 500, replace=F)
s2 <- sample(1:nrow(df), size= 500, replace=F)
return(rbind(df[-s1,], df[s2,]))
}
dt_m <- df_m <- vector("numeric", length= 500L)
for (i in 1:500) {
Rprof("./DF_mem.out", memory.profiling = TRUE)
y <- prof_func(df1)
Rprof(NULL)
df <- summaryRprof("./DF_mem.out", memory= "both")
df_m[i] <- df$by.self$mem.total[which(rownames(df$by.self) == "\"rbind\"")]
Rprof("./DT_mem.out", memory.profiling = TRUE)
y2 <- prof_func(df2)
Rprof(NULL)
dt <- summaryRprof("./DT_mem.out", memory = "both")
dt_m[i] <- dt$by.self$mem.total[which(rownames(dt$by.self) == "\"rbind\"")]
}
pryr::object_size(df1)
80 MB
pryr::object_size(df2)
80 MB
# EDITED: via typo / fix from @Joshua Ulrich.
# improvement in memory usage via DT. still not pass-by-reference
quantile(df_m, seq(0,1,.1))
0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
379.00 428.60 440.10 447.70 455.36 459.20 466.48 469.89 474.40 482.10 512.60
quantile(dt_m, seq(0,1,.1))
0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
76.80 84.50 84.50 92.10 92.10 92.10 92.10 107.30 116.46 130.20 157.00
附录:
### speed improvement:
#-----------------------------------------------
library(data.table)
library(microbenchmark)
set.seed(444L)
df1 <- data.frame(matrix(rnorm(1e7), ncol= 10))
df2 <- data.table(matrix(rnorm(1e7), ncol= 10))
microbenchmark(
df= {
s1 <- sample(1:nrow(df1), size= 500, replace=F)
s2 <- sample(1:nrow(df1), size= 500, replace=F)
df1 <- rbind(df1[-s1,], df1[s2,])
},
dt= {
s1 <- sample(1:nrow(df2), size= 500, replace=F)
s2 <- sample(1:nrow(df2), size= 500, replace=F)
df2 <- rbind(df2[-s1,], df2[s2,])
}, times= 100L)
Unit: milliseconds
expr min lq mean median uq max neval cld
df 672.5106 757.65188 814.1582 809.6346 864.6668 998.2290 100 b
dt 68.1254 85.73178 139.1256 120.3613 148.8243 397.7359 100 a
prof_func
有错误。它在 df1
上调用 rbind
而不是它的参数 (df
)。解决这个问题,您将看到 data.table 对象的内存使用量减少。
library(data.table)
set.seed(444L)
df1 <- data.frame(matrix(rnorm(1e7), ncol= 10))
df2 <- data.table(matrix(rnorm(1e7), ncol= 10))
prof_func <- function(df) {
s1 <- sample(1:nrow(df), size= 500, replace=F)
s2 <- sample(1:nrow(df), size= 500, replace=F)
return(rbind(df[-s1,], df[s2,]))
}
dt_m <- df_m <- vector("numeric", length= 500L)
for (i in 1:100) {
Rprof("./DF_mem.out", memory.profiling = TRUE, interval=0.01)
y <- prof_func(df1)
Rprof(NULL)
df <- summaryRprof("./DF_mem.out", memory= "both")
df_m[i] <- df$by.total["\"rbind\"","mem.total"]
Rprof("./DT_mem.out", memory.profiling = TRUE, interval=0.01)
y2 <- prof_func(df2)
Rprof(NULL)
dt <- summaryRprof("./DT_mem.out", memory = "both")
dt_m[i] <- dt$by.total["\"rbind\"","mem.total"]
}
quantile(df_m, seq(0,1,.1))
# 0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
# 0.0 0.0 0.0 0.0 0.0 0.0 0.0 413.4 432.5 455.0 485.9
quantile(dt_m, seq(0,1,.1))
# 0% 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
# 0.0 0.0 0.0 0.0 0.0 0.0 0.0 53.9 84.5 122.6 153.1