首次括号内的分配与完整分配一样耗时?

Fer*_*aft 8 copy r lazy-evaluation

关于这个答案: R中的复制修改语义到底是什么,以及规范来源在哪里?

我们可以看到,在第一次改变'[<-'矢量时,即使只修改一个条目,R也会复制整个矢量.然而,在第二次,矢量"就地"改变.如果我们测量创建和修改大向量的时间,则在不检查对象的地址的情况下这是显而易见的:

> system.time(a <- rep(1L, 10^8))
   user  system elapsed 
   0.15    0.17    0.31 
> system.time(a[222L] <- 111L)
   user  system elapsed 
   0.26    0.08    0.34 
> system.time(a[333L] <- 111L)
   user  system elapsed 
      0       0       0
Run Code Online (Sandbox Code Playgroud)

请注意,类型/ storage.mode没有变化.

所以问题是:为什么不能优化第一个括号分配呢?在什么情况下实际需要这种行为(第一次修改时的完整拷贝)?

编辑:(剧透!)正如下面接受的答案中所解释的,这只不过是在system.time函数调用中包含第一个赋值的工件.这导致R将绑定的内存空间标记a为可能引用多个符号,因此在更改时需要重复.如果我们删除封闭调用,则会从第一个括号分配中修改向量.

感谢Martin提供深入的解决方案!

Mar*_*gan 9

比较"NAM()"部分

> a <- rep(1L, 10)
> .Internal(inspect(a))
@457b840 13 INTSXP g0c4 [NAM(1)] (len=10, tl=0) 1,1,1,1,1,...
Run Code Online (Sandbox Code Playgroud)

> system.time(a <- rep(1L, 10))
[...]
> .Internal(inspect(a))
@4626f88 13 INTSXP g0c4 [NAM(2)] (len=10, tl=0) 1,1,1,1,1,...
Run Code Online (Sandbox Code Playgroud)

第一个例子中的"1"表示R认为有一个引用a,因此可以就地更新."2"表示R认为至少有两个引用a,因此如果修改则需要重复.粗略地说,我把它理性化为rep()内部返回值的表示system.time,以及它在外部的价值system.time; 道德等同于f = function() { x <- rep(1L, 10); x }; a = f()而不是g = function() rep(1L, 10); a = g().

真实世界的代码a <- rep(1L, 10^8); a[123L] <- 231L不涉及副本.我们可以在不人为地增加NAMED计数的情况下为作业计时

> a <- rep(1L, 10^8)
> .Internal(inspect(a))
@7f972b571010 13 INTSXP g0c7 [NAM(1)] (len=100000000, tl=0) 1,1,1,1,1,...
> system.time(a[123L] <- a[321L])
   user  system elapsed 
      0       0       0 
Run Code Online (Sandbox Code Playgroud)