附加到 R 中的列表会导致复制吗?
Posted
技术标签:
【中文标题】附加到 R 中的列表会导致复制吗?【英文标题】:Does appending to a list in R result in copying? 【发布时间】:2012-09-28 03:51:10 【问题描述】:假设我在 R 中创建了一个列表并将其追加如下:
x = list(10)
x[[2]] = 20
这是否等同于
x = list(10)
x = list(10, 20)
?我对 R 如何处理内存中的列表的特定细节不是很有经验,但我有限的理解是它往往是复制快乐的;对我来说理想的是第一个选项本质上不涉及在内存中创建另一个列表,而只是导致在内存中为附加值留出一个新位置。本质上,如果我有一个很大的列表,我不想让 R 复制它,如果我只是想在它上面附加一些东西。
如果我想要的行为不是这里给出的,有没有其他方法可以得到想要的效果?
【问题讨论】:
也许?tracemem
有用吗?
和.Internal(inspect(x))
前后。
【参考方案1】:
我相当有信心答案是“不”。我使用以下代码仔细检查:
Rprof(tmp <- tempfile(), memory.profiling = TRUE)
x <- list()
for (i in 1:100) x[[i]] <- runif(10000)
Rprof()
summaryRprof(tmp, memory = "stats")
unlink(tmp)
输出:
# index: runif
# vsize.small max.vsize.small vsize.large max.vsize.large
# 76411 381781 424523 1504387
# nodes max.nodes duplications tot.duplications
# 2725878 13583136 0 0
# samples
# 5
相关部分为duplications = 0
。
【讨论】:
我认为您的推理不一定正确:重复在 R 中具有特殊含义,从技术上讲,虽然扩展向量的长度会创建副本,但它不是重复。请参阅 R-help 上的此线程:r.789695.n4.nabble.com/Understanding-tracemem-td4636321.html【参考方案2】:Matthew Dowle 的回答 here 以及提高内存效率背后的基本原理是通过 <-
、[<-
、[[<-
和其他基本 R
操作(names
等)阻止大量幕后复制
[[<-
将复制整个x
。请参阅下面的示例
x <- list(20)
tracemem(x)
#[1] "<0x2b0e2790>"
x[[2]] <- 20
# tracemem[0x2b0e2790 -> 0x2adb7798]:
你的第二个案例
x <- list(10,20)
并不是真正附加原始 x
,而是将 x
替换为恰好是原始 x
的对象,并带有附加值。
【讨论】:
(+1),第二种情况不是附加的,也不是我提议的示例,而是我不希望 R 在幕后做的示例。 啊,我误读了你的问题,当你问x <- list(10,20)
是否等同于(就内存而言)x <- list(10); x[[2]] <- 20
时,它首先读给我听。在重读时,我发现它比这更微妙。
是的,但在那个链接的答案中,x
是data.frame
。在这个问题中x
是list
。 list
的复制行为可能不同。请注意,没有[<-.list
方法,但有[<-.data.frame
。使用.Internal(inspect(x))
查看。【参考方案3】:
为了帮助我弄清楚修改列表是否会产生深拷贝或浅拷贝,我设置了一个小实验。如果修改列表会产生深拷贝,那么与包含小对象的列表相比,修改包含大对象的列表应该会更慢:
z1 <- list(runif(1e7))
z2 <- list(1:10)
system.time(
for(i in 1:1e4) z1[1 + i] <- 1L
)
# user system elapsed
# 0.283 0.034 0.317
system.time(
for(i in 1:1e4) z2[1 + i] <- 1L
)
# user system elapsed
# 0.284 0.034 0.319
我的计算机上的时间基本相同,这表明复制列表会产生浅拷贝,复制指向现有数据结构的指针。
【讨论】:
.Internal(inspect(x))
是一种更具体的表达方式。查看长向量的十六进制地址是否已更改。【参考方案4】:
接受了 flodel 的回答,但 Chase 的提示很好,所以我使用他使用 tracemem()
的建议确认我有所需的行为。这是第一个示例,我们只是追加到列表中:
x = list(10)
tracemem(x[[1]])
# [1] "<0x2d03fa8>" #(likely different on each machine)
x[[2]] = 20
tracemem(x[[1]])
# [1] "<0x2d03fa8>"
这是第二个示例的结果,我们在其中创建了两个列表:
x = list(10)
tracemem(x[[1]])
# [1] "<0x2d03c78>"
x = list(10, 20)
tracemem(x[[1]])
# [1] "<0x2d07ff8>"
所以第一种方法似乎给出了所需的行为。
【讨论】:
以上是关于附加到 R 中的列表会导致复制吗?的主要内容,如果未能解决你的问题,请参考以下文章