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

She*_*hen 10 r

替换矢量中的元素时

a <- 1:1000000
a[1] <- 2
Run Code Online (Sandbox Code Playgroud)

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

谢谢

Mar*_*gan 21

tracemem功能(R需要编译,以支持它)提供当复制发生的指示.这是你做的

> a <- 1:1000000; tracemem(a)
[1] "<0x7f791b39e010>"
> a[1] = 2
tracemem[0x7f791b39e010 -> 0x7f791a9d4010]: 
Run Code Online (Sandbox Code Playgroud)

确实有副本.但这是因为你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
>
Run Code Online (Sandbox Code Playgroud)

进一步的洞察力来自于在表面层面理解R的内存管理是如何工作的.每个分配都有与之关联的NAMED级别.NAMED = 0或1表示最多有1个符号引用它; 因此可以安全地复制到位.NAMED = 2表示存在或已经存在至少2个指向同一位置的符号,并且任何更新值的尝试都需要重复以保留R的"复制时复制"的错觉.以下揭示了一些内部结构a,包括INTSXP(整数)类型与NAM(1)(NAMED级别1)并且它是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
> 
Run Code Online (Sandbox Code Playgroud)

另一方面,这里有两个符号表示内存中的位置,因此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]: 
Run Code Online (Sandbox Code Playgroud)

关于NAMED很难推理,所以在某种程度上这些类型的游戏对它们有一定程度的徒劳.

inspect返回其他信息.每个R类型在内部表示为"SEXP"(S表达式)类型.这些是枚举,第13个SEXP类型是整数SEXP - 因此13 INTSXP.检查.Internal(inspect(...))数字向量,字符向量,甚至函数.Internal(inspect(function() {})).

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

所述TR表示中的位在SEXP以指示该变量被跟踪集合; 我们说的时候就定了tracemem(a).

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


小智 6

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

require(ff)
a <- ff(1:1000000)
a[1] <- 2
Run Code Online (Sandbox Code Playgroud)

有关信息.这些是时间,因此对于您的玩具箱而言要快得多.

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 
Run Code Online (Sandbox Code Playgroud)

  • @Shen如果你加载`microbenchmark`软件包,很容易为你的特定代码变化运行时序测试. (2认同)