按照 Joshua 的评论进行编辑:下面显示的行为仅限于 R-studio!
为了回答 OP 的问题,复制的根本原因(正如@MartinMorgan 解释的那样)是由于a
. 如果第一个命令不包含 a system.time(.)
,则a <- rep(1, 10^8)
返回一个NAM(1)
类型,该类型将导致两个分配都没有副本。
R-studio 中的观察:
但是,要指出另一个有趣的观察/差异,如果您要在 R-studio 中运行,您可能不知道还有一个额外的行为差异(与 R64/R32 会话的差异)。
差异(在 R 工作室中)似乎源于您运行代码的方式。也就是说,如果您一次复制并粘贴所有内容(如下所示,包括输出):
system.time(a <- rep(1L, 10^8))
# user system elapsed
# 0.256 0.263 0.526
.Internal(inspect(a))
# @10745d000 13 INTSXP g0c7 [NAM(2)] (len=100000000, tl=0) 1,1,1,1,1,...
system.time(a[222L] <- 111L)
# user system elapsed
# 0.299 0.199 0.498
.Internal(inspect(a))
# @11f1d6000 13 INTSXP g0c7 [NAM(1)] (len=100000000, tl=0) 1,1,1,1,1,...
system.time(a[333L] <- 111L)
# user system elapsed
# 0 0 0
.Internal(inspect(a))
# @11f1d6000 13 INTSXP g1c7 [MARK,NAM(1)] (len=100000000, tl=0) 1,1,1,1,1,...
您会看到第二次分配不涉及内存副本,所需时间为 0 秒。现在,复制/粘贴/执行同一组命令,但现在是一个接一个(在输入下一行之前在每一行之后按回车键)。结果如下:
system.time(a <- rep(1L, 10^8))
# user system elapsed
# 0.256 0.265 0.588
>
.Internal(inspect(a))
# @10745d000 13 INTSXP g0c7 [NAM(2)] (len=100000000, tl=0) 1,1,1,1,1,...
system.time(a[222L] <- 111L)
# user system elapsed
# 0.302 0.204 0.559
.Internal(inspect(a))
# @11f1d6000 13 INTSXP g0c7 [NAM(2)] (len=100000000, tl=0) 1,1,1,1,1,...
system.time(a[333L] <- 111L)
# user system elapsed
# 0.296 0.208 0.504
>
.Internal(inspect(a))
# @10745d000 13 INTSXP g0c7 [NAM(2)] (len=100000000, tl=0) 1,1,1,1,1,...
对于相同的语法,这里正在制作副本,运行时间为 0.5 秒。
现在解释差异(正如@MartinMorgan 在他的回答中解释的那样):
对于第一种情况,作为 NAM(2) SEXP 对象,它在分配期间被复制。但是,当您一次运行所有行时,这仅在第一种情况下发生一次。还要注意的是,第二个赋值有一个 MARK (unsigned int) 表示“将对象标记为正在使用”(来自R-internals)。
在第二种情况下,在 R-studio 中,对每一行按 Enter 会导致这些分配中的每一个都返回一个 NAM(2) SEXP 对象。因此,每次都在制作副本。