我做了这个
> x = 1:5
> .Internal(inspect(x))
@3acfed60 13 INTSXP g0c3 [NAM(1)] (len=5, tl=0) 1,2,3,4,5
> x[] = cumsum(x)
> .Internal(inspect(x))
@3acfed60 13 INTSXP g0c3 [NAM(1)] (len=5, tl=0) 1,3,6,10,15
其中@3acfed60
是(共享)内存地址。关键是 NAM(1),它表示只有一个对 x 的引用,因此不需要在更新时重新分配。
R 使用(目前,我认为这将在下一个版本中改变)一个引用计数版本,其中 R 符号被引用 0、1 或超过 1 次;当一个对象被多次引用时,它的引用计数不能减少(因为“多于一个”可能意味着 3,因此无法区分 2 个引用和 3 个引用,因此无法区分一个小于 2并且小于 3)。任何修改尝试都需要重复。
最初我忘记加载 pryr 并编写了我自己的address()
> address = function(x) .Internal(inspect(x))
这揭示了一个有趣的问题
> x = 1:5
> address(x)
@4647128 13 INTSXP g0c3 [NAM(2)] (len=5, tl=0) 1,2,3,4,5
> x[] = cumsum(x)
> address(x)
@4647098 13 INTSXP g0c3 [NAM(2)] (len=5, tl=0) 1,3,6,10,15
注意NAM(2)
,它表示函数内部至少有两个对 的引用x
,即在全局环境中和在函数环境中。因此,触摸x
函数内部会触发未来的重复,有点像海森堡不确定性原理。cumsum
(and .Internal
, and length
) 的编写方式允许在不增加 NAMED 的情况下进行引用;address()
应修改为具有类似的行为(现已修复)
嗯,当我深入挖掘时,我看到(我想这很明显,回想起来)实际发生的是cumsum(x)
确实通过 S 表达式分配内存
> x = 1:5
> .Internal(inspect(x))
@3bb1cd0 13 INTSXP g0c3 [NAM(1)] (len=5, tl=0) 1,2,3,4,5
> .Internal(inspect(cumsum(x)))
@43919d0 13 INTSXP g0c3 [] (len=5, tl=0) 1,3,6,10,15
但是该分配x[] <-
将新内存与旧位置(??)相关联。(这似乎与 data.table 一样“高效”,它显然也为 cumsum 创建了一个 S 表达式,大概是因为它本身调用了 cumsum!)所以大多数情况下我对这个答案没有帮助......
分配本身不太可能导致性能问题,而是gcinfo(TRUE)
不再使用的内存的垃圾收集(查看这些)。我发现启动 R 很有用
R --no-save --quiet --min-vsize=2048M --min-nsize=45M
从更大的内存池开始,因此更少的(初始)垃圾收集。分析您的编码风格以了解为什么您认为这是性能瓶颈会很有用。