在R中,向现有列表追加元素的最有效的内存方式是什么?

mftmpeh8  于 2023-05-20  发布在  其他
关注(0)|答案(2)|浏览(90)

我在R中有一个列表,在下面的例子中是my_list2
我想以最小化RAM使用峰值的方式将项目添加到列表中。
有没有比使用append函数更有效的内存方式来实现这一点?
我知道最好的做法是创建一个“空”列表,然后按照下面的例子中的my_list2填充它,但这不是一个选项,因为列表已经存在。

# If I could create the list from scratch I'd do it list this:
my_list <- vector('list', 10)
for (i in 1:10) {
  my_list[[i]] <- i
}

# Is there a better way than the 'append' function?
my_list2 <- list(1)
for (i in 2:10) {
  my_list2 <- append(my_list2, i)
}
xtfmy6hx

xtfmy6hx1#

您可以创建一个临时列表,并在最后只将其追加到my_list2一次,而不是在每次迭代中都使用append()。这对你有用吗?
下面是一个for循环中5k次迭代的例子:

my_list <- list(1)
my_list2 <- list(1)

bench::mark(
  orig = {
    for (i in 2:5000) {
      my_list <- append(my_list, i)
    }
    my_list
  },
  mine = {
    tmp <- vector("list", 4999)
    for (i in 1:4999) {
      tmp[[i]] <- i + 1
    }
    append(my_list2, tmp)
  },
  iterations = 10
)
#> Warning: Some expressions had a GC in every iteration; so filtering is
#> disabled.
#> # A tibble: 2 × 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 orig       420.01ms    1.69s     0.567    95.7MB     13.6
#> 2 mine         1.52ms      2ms   406.       96.8KB      0

请注意,bench::mark()会自动检查两个代码是否给予相同的输出。

w8f9ii69

w8f9ii692#

具有低峰值RAM使用率的实用解决方案如下所示:

my_list <- list(1)
N <- length(my_list)
length(my_list) <- N + 9
for (i in 2:10) {
  my_list[[N + i -1]] <- i
  #gc() #Optional
}

您可以使用gc来获取内存使用峰值。但是这在很大程度上取决于在执行期间是否存在垃圾收集。要查看可能的最小峰值gctorture,可以打开,但执行时间通常会慢得多。由于调用方法的顺序可能会影响结果,因此每次都启动一个新的vanilla会话。

#Using append
n <- 1e5
gctorture(on=TRUE)

set.seed(0)
L <- list(sample(n))
gc(reset=TRUE)
#         used (Mb) gc trigger (Mb) max used (Mb)
#Ncells 285638 15.3     664228 35.5   285638 15.3
#Vcells 633121  4.9    8388608 64.0   633121  4.9
for (i in 2:10) L <- append(L, list(sample(n)))
gc()
#          used (Mb) gc trigger (Mb) max used (Mb)
#Ncells  344156 18.4     664228 35.5   345174 18.5
#Vcells 1215086  9.3    8388608 64.0  1265554  9.7
#Using [[<-
n <- 1e5
gctorture(on=TRUE)

set.seed(0)
L <- list(sample(n))
gc(reset=TRUE)
#         used (Mb) gc trigger (Mb) max used (Mb)
#Ncells 285638 15.3     664228 35.5   285638 15.3
#Vcells 633121  4.9    8388608 64.0   633121  4.9
for (i in 2:10) L[[length(L)+1]] <- sample(n)
gc()
#          used (Mb) gc trigger (Mb) max used (Mb)
#Ncells  346937 18.6     664228 35.5   347919 18.6
#Vcells 1221639  9.4    8388608 64.0  1272088  9.8
#Using [[<- but resizing the list before
n <- 1e5
gctorture(on=TRUE)

set.seed(0)
L <- list(sample(n))
gc(reset=TRUE)
#         used (Mb) gc trigger (Mb) max used (Mb)
#Ncells 285638 15.3     664228 35.5   285638 15.3
#Vcells 633121  4.9    8388608 64.0   633121  4.9
N <- length(L)
length(L) <- N + 9
for (i in 2:10) L[[N - 1 + i]] <- sample(n)
gc()
#          used (Mb) gc trigger (Mb) max used (Mb)
#Ncells  346564 18.6     664228 35.5   347498 18.6
#Vcells 1220761  9.4    8388608 64.0  1271479  9.8

这里append需要8.0 Mb,[[<-需要8.2 Mb,无论列表大小是否增加。
执行相同的操作,但不使用gctorture,而是在每一步之后手动使用gc,得到:

#Using append
n <- 1e5

set.seed(0)
L <- list(sample(n))
gc(reset=TRUE)
#         used (Mb) gc trigger (Mb) max used (Mb)
#Ncells 285638 15.3     664228 35.5   285638 15.3
#Vcells 633121  4.9    8388608 64.0   633121  4.9
for (i in 2:10) {L <- append(L, list(sample(n))); gc()}
gc()
#          used (Mb) gc trigger (Mb) max used (Mb)
#Ncells  344145 18.4     664228 35.5   372952 20.0
#Vcells 1215054  9.3    8388608 64.0  1319826 10.1
#Using [[<-
n <- 1e5

set.seed(0)
L <- list(sample(n))
gc(reset=TRUE)
#         used (Mb) gc trigger (Mb) max used (Mb)
#Ncells 285638 15.3     664228 35.5   285638 15.3
#Vcells 633121  4.9    8388608 64.0   633121  4.9
for (i in 2:10) {L[[length(L)+1]] <- sample(n); gc()}
gc()
#          used (Mb) gc trigger (Mb) max used (Mb)
#Ncells  346926 18.6     664228 35.5   377474 20.2
#Vcells 1221607  9.4    8388608 64.0  1352555 10.4
n <- 1e5

set.seed(0)
L <- list(sample(n))
gc(reset=TRUE)
#         used (Mb) gc trigger (Mb) max used (Mb)
#Ncells 285638 15.3     664228 35.5   285638 15.3
#Vcells 633121  4.9    8388608 64.0   633121  4.9
N <- length(L)
length(L) <- N + 9
for (i in 2:10) {L[[N - 1 + i]] <- sample(n); gc()}
gc()
#          used (Mb) gc trigger (Mb) max used (Mb)
#Ncells  347659 18.6     664771 35.6   374526 20.1
#Vcells 1223042  9.4    8388608 64.0  1273592  9.8

这里append需要9.9 Mb,[[<-在不预先调整列表大小的情况下需要10.4 Mb,并且当列表大小增加到9.7 Mb之前时。
如果你想知道分配的内存总量,但同时也想知道释放的内存或其他选项,可以看看Monitor memory usage in R

相关问题