我对它做了一些实验,发现 R 总是在第一次修改下复制对象。
你可以在我的机器上看到结果http://rpubs.com/wush978/5916
如果我犯了任何错误,请告诉我,谢谢。
测试一个对象是否被复制
我使用以下 C 代码转储内存地址:
#define USE_RINTERNALS
#include <R.h>
#include <Rdefines.h>
SEXP dump_address(SEXP src) {
Rprintf("%16p %16p %d\n", &(src->u), INTEGER(src), INTEGER(src) - (int*)&(src->u));
return R_NilValue;
}
它将打印 2 个地址:
让我们编译并加载这个 C 函数。
Rcpp:::SHLIB("dump_address.c")
dyn.load("dump_address.so")
会话信息
这里是sessionInfo
测试环境。
sessionInfo()
写时复制
首先我测试了copy on write的属性,这意味着 R 仅在对象被修改时才复制它。
a <- 1L
b <- a
invisible(.Call("dump_address", a))
invisible(.Call("dump_address", b))
b <- b + 1
invisible(.Call("dump_address", b))
对象b
从修改处复制a
。R 确实实现了该copy on write
属性。
就地修改向量/矩阵
然后我测试当我们修改向量/矩阵的元素时 R 是否会复制对象。
长度为 1 的向量
a <- 1L
invisible(.Call("dump_address", a))
a <- 1L
invisible(.Call("dump_address", a))
a[1] <- 1L
invisible(.Call("dump_address", a))
a <- 2L
invisible(.Call("dump_address", a))
地址每次都会改变,这意味着 R 不会重用内存。
长向量
system.time(a <- rep(1L, 10^7))
invisible(.Call("dump_address", a))
system.time(a[1] <- 1L)
invisible(.Call("dump_address", a))
system.time(a[1] <- 1L)
invisible(.Call("dump_address", a))
system.time(a[1] <- 2L)
invisible(.Call("dump_address", a))
对于长向量,R 在第一次修改后重用内存。
而且,上面的例子还表明,当对象很大时,“就地修改”确实会影响性能。
矩阵
system.time(a <- matrix(0L, 3162, 3162))
invisible(.Call("dump_address", a))
system.time(a[1,1] <- 0L)
invisible(.Call("dump_address", a))
system.time(a[1,1] <- 1L)
invisible(.Call("dump_address", a))
system.time(a[1] <- 2L)
invisible(.Call("dump_address", a))
system.time(a[1] <- 2L)
invisible(.Call("dump_address", a))
似乎 R 仅在第一次修改时复制对象。
我不知道为什么。
改变属性
system.time(a <- vector("integer", 10^2))
invisible(.Call("dump_address", a))
system.time(names(a) <- paste(1:(10^2)))
invisible(.Call("dump_address", a))
system.time(names(a) <- paste(1:(10^2)))
invisible(.Call("dump_address", a))
system.time(names(a) <- paste(1:(10^2) + 1))
invisible(.Call("dump_address", a))
结果是一样的。R 仅在第一次修改时复制对象。