替换矢量中的元素时,例如
a <- 1:1000000
a[1] <- 2
R复制整个向量,替换新向量中的元素,然后执行变量名称重新关联。我想知道无论如何要覆盖或阻止它使它表现得更像c阵列?
由于
答案 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