【发布时间】:2019-07-21 01:16:18
【问题描述】:
提到here R 在将变量分配给新变量时使用 copy-on-modify,包括将参数传递给函数。
但是,切片(vector、list、data frame)会创建一个相同类型的新对象,其中包含原始对象子集的 oopies,还是存储在新对象中的元素原始文件的副本还是只是修改时复制的参考?
【问题讨论】:
标签: r slice copy-on-write
提到here R 在将变量分配给新变量时使用 copy-on-modify,包括将参数传递给函数。
但是,切片(vector、list、data frame)会创建一个相同类型的新对象,其中包含原始对象子集的 oopies,还是存储在新对象中的元素原始文件的副本还是只是修改时复制的参考?
【问题讨论】:
标签: r slice copy-on-write
这是一个复杂的话题。您应该从阅读NAMED mechanism 开始。
如果您运行以下命令,您会看到没有列表元素的副本(因为列表基本上是指向其元素的指针):
> a <- list(1, 2, 3, 4, 5)
>
> b <- a[1:2]
> .Internal(inspect(b))
@0x000000001327e5b8 19 VECSXP g0c2 [NAM(3)] (len=2, tl=0)
@0x00000000136f6b60 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 1
@0x00000000136f6b28 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 2
>
>
> c <- a[1:2]
> .Internal(inspect(c))
@0x000000001327e678 19 VECSXP g0c2 [NAM(3)] (len=2, tl=0)
@0x00000000136f6b60 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 1
@0x00000000136f6b28 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 2
>
> b[1] <- 6
> .Internal(inspect(b))
@0x000000001327e6f8 19 VECSXP g0c2 [NAM(1)] (len=2, tl=0)
@0x0000000013745b58 14 REALSXP g0c1 [] (len=1, tl=0) 6
@0x00000000136f6b28 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 2
>
> .Internal(inspect(c))
@0x000000001327e678 19 VECSXP g0c2 [NAM(3)] (len=2, tl=0)
@0x00000000136f6b60 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 1
@0x00000000136f6b28 14 REALSXP g0c1 [NAM(3)] (len=1, tl=0) 2
如果你对向量进行子集化,情况就不同了。
您可能还对new reference counting mechanism 感兴趣。
【讨论】:
vector 与list 不同。它们将所有元素存储在一个内存块中,而不是一个一个地存储,因为它们的类型已知为什么会创建 vectors,这可以最大限度地减少碎片,改善局部性,并且可能分配更少的空间,因为只有一种类型要记录。
shallow copy...我也想知道,这是否会发生在R 中的任何其他容器上(如使用定义的容器)?
将一个原子向量子化为一个较短的向量会给你一个新的向量。将对象中的整个向量子集为您提供修改时复制参考。这样做的结果是,您可以进行子集化以获得一个新的较短列表对象,但在您修改之前,它的内容将引用原始列表中的内容(没有总体内存成本)。
有关内存管理的更多详细信息,请参阅Hadley's notes。
【讨论】:
可能与 python 不同,R 会在您切片时创建新对象。例如:
> a=c(1,2,3,4,5)
> a
[1] 1 2 3 4 5
> b=a[1]
> b
[1] 1
> b=7
> b
[1] 7
> a
[1] 1 2 3 4 5
这同样适用于vectors、lists 或dataframes。查看this 中的R 中的引用对象的帖子。
【讨论】:
当对向量进行子集化时,R 不会执行 copy-on-modify ,但它会从我们从中创建子集的对象创建另一个对象。例如:
library(lobstr)
> x = rnorm(10)
# tracking object copy:
> tracemem(x)
[1] "<0xcad0728>"
> y = x # shared binding
> ref(x, y)
[1:0xcad0728] <dbl>
[1:0xcad0728]
# So, x and y points/refers to the same object at 0xcad0728. That is, we have a shared binding to the same object
# Now let's subsetting:
> y = y[1:5] # no warning from tracemem(). So, no copy-on-modify behavior triggered
# now let's check the memory addrs:
> obj_addr(x)
[1] "0xcad0728"
> obj_addr(y)
[1] "0xcd1e198"
正如我们所见,tracemem() 函数没有关于对象复制的信号。但是,subsetting 确实在内存中创建了另一个对象,因此名称 y 不再指向 0xcad0728,而是指向 0xcad0728.
【讨论】: