替换元素时避免复制整个向量(a [1]< - 2)

时间:2013-08-21 14:17:05

标签: r

替换矢量中的元素时,例如

a <- 1:1000000
a[1] <- 2

R复制整个向量,替换新向量中的元素,然后执行变量名称重新关联。我想知道无论如何要覆盖或阻止它使它表现得更像c阵列?

由于

2 个答案:

答案 0 :(得分:21)

tracemem函数(R需要编译以支持它)提供复制何时发生的指示。这就是你做的事情

> a <- 1:1000000; tracemem(a)
[1] "<0x7f791b39e010>"
> a[1] = 2
tracemem[0x7f791b39e010 -> 0x7f791a9d4010]: 

确实有副本。但这是因为您从一个整数向量(a创建一个整数序列)强制1:1000000到一个数字向量(因为2是一个数值,并且R强制一般的类型)。相反,如果用整数值或带数值的数字向量更新整数向量,则没有复制

> a <- 1:1000000; tracemem(a)
[1] "<0x7f791a4ef010>"
> a[1] = 2L
> a = c(1, 2, 3); tracemem(a)
[1] "<0x5180470>"
> a[1] = 2
>

从表面层面理解R的记忆管理是如何运作的,还有一点点进一步的洞察力。每个分配都有与之关联的NAMED级别。 NAMED = 0或1表示最多有1个符号引用它;因此可以安全地复制到位。 NAMED = 2表示存在或者至少有2个符号指向同一位置,并且任何更新该值的尝试都需要重复以保留R&#39;复制变更的错觉&# 39 ;.以下揭示了a的一些内部结构,包括具有NAM(1)(NAMED级别1)的INTSXP(整数)类型以及它是TRaced。因此更新(使用整数!)不需要副本。

> a = 1:10; tracemem(a); .Internal(inspect(a))
[1] "<0x5170818>"
@5170818 13 INTSXP g0c4 [NAM(1),TR] (len=10, tl=0) 1,2,3,4,5,...
> a[1] = 2L
> 

另一方面,这里有两个符号表示内存中的位置,因此NAMED为2且需要复制

> a = b = 1:10; tracemem(a); .Internal(inspect(a))
[1] "<0x576d1a0>"
@576d1a0 13 INTSXP g0c4 [NAM(2),TR] (len=10, tl=0) 1,2,3,4,5,...
> a[1] = 2L
tracemem[0x576d1a0 -> 0x576d148]: 

很难对NAMED进行推理,所以在某种程度上这些类型的游戏对它们来说都是徒劳的。

inspect会返回其他信息。每种R类型在内部表示为SEXP&#39; (S表达式)类型。这些是枚举,第13个SEXP类型是整数SEXP - 因此13 INTSXP。查看.Internal(inspect(...))以获取数字向量,字符向量甚至函数.Internal(inspect(function() {}))

R通过定期运行“垃圾收集器”来管理内存。检查是否当前引用了内存;如果不是,则将其回收以供另一个符号使用。垃圾收集器是“世代”,这意味着最近分配的内存比旧内存更频繁地检查回收(这是因为,根据经验,变量往往具有较短的半衰期,例如,持续时间函数调用,所以最近分配的内存更有可能用于回收,而不是长时间使用的内存。 g0c4和类似的注释提供了有关SEXP所属的生成的信息。

TR代表&#39;位&#39;在SEXP中设置以指示正在跟踪变量;它是在我们说tracemem(a)时设定的。

其中一些主题在R的内部实现RShowDoc("R-ints")和C头文件Rinternals.h的文档中进行了讨论。

答案 1 :(得分:6)

您可以使用CRAN上的ff包执行此操作。使用ff,您的数据存储在磁盘上,索引只会影响您正在编制索引的特定元素

require(ff)
a <- ff(1:1000000)
a[1] <- 2

有关信息。这些是时间安排,所以玩具箱的速度要快得多。

require(ff)
a <- 1:100000000
b <- ff(a)
system.time(a[1] <- 2)
 user  system elapsed 
0.440   0.592   1.056 
system.time(b[1] <- 2)
 user  system elapsed 
0.004   0.000   0.001