【问题标题】:How is memory managed while overwriting R objects?覆盖 R 对象时如何管理内存?
【发布时间】:2015-07-10 01:16:43
【问题描述】:

我正在处理一些大型数据集,并且正在尽我所能保持在 R 的内存限制之下。出现了一个关于覆盖 R 对象的问题。我有一个大的data.table(或任何 R 对象),它必须多次复制到tmp。问题是:如果我在覆盖之前删除 tmp 有什么不同吗?在代码中:

for (1:lots_of_times) {
     v_l_d_t_tmp <- copy(very_large_data_table) # Necessary copy of 7GB data
                                                # table on 16GB machine. I can
                                                # afford 2 but not 3 copies.
     ### do stuff to v_l_d_t_tmp and output
     rm (v_l_d_t_tmp)  # The question is whether this rm keeps max memory
                        # usage lower, or if it is equivalent to what an 
                        # overwrite will automatically do on the next iteration.
}

假设副本是必要的(如果我需要在每个循环中从磁盘读取very_large_data_table,我会这样做,但问题是:如果我这样做,它会对最大内存使用量有什么影响吗?在再次加载之前明确删除v_l_d_t_tmp?)。

或者,为了教这个人钓鱼,我可以输入什么(在 R 中,我们不要进入 ps)来自己回答这个问题?

如果答案是:“信任垃圾收集”,那完全没问题。

【问题讨论】:

  • 不是专家,但应该没什么区别。在任何一种情况下,原始内存位置都不再由符号指向,因此有资格进行垃圾收集(很高兴得到专家的纠正)。要考虑的一件事是您的data.table 是否真的需要复制。它旨在通过引用进行修改,因此您不必这样做。
  • 正如@BrodieG 所说,使用rm 告诉R 您不再需要访问v_l_d_t_tmp,并且该表占用的内存可以进行垃圾回收。但是,当您调用rm 时不会进行垃圾收集,而是在需要时“自动”进行。这并不总是很有效,因此您可以在调用rm(...) 之后立即调用gc() 来强制进行垃圾回收。这可能会使您的循环工作。但是,根据我的经验,您确实需要重新启动 R 才能正确清除内存(这意味着每次迭代都读取您的输入,可能很慢)。
  • 根据我自己的经验 - 信任垃圾收集器。我们解析了基因组数据,并用新的数据帧覆盖了所有数据帧,但我没有支持它的理论背景......
  • @konvas 我也有类似的经历...请参阅我的答案,它重新解决了问题以避免内存分配问题。

标签: r performance memory bigdata


【解决方案1】:

这是另一个想法......它不会直接回答您的问题,而是尝试通过以另一种方式消除内存问题来解决它。可能会让你思考:

如果你改为缓存very_large_data_table,然后只读取一次,做你需要做的,然后退出R。现在,在R之外写一个循环,然后内存问题消失。当然,这会花费您更多的 CPU,因为您必须多次读取 7GB 内存……但节省内存成本可能是值得的。事实上,这将您的内存使用量减半,因为您不必复制表。

此外,就像 @konvas 在 cmets 中指出的那样,我也发现 rm() 即使使用 gc() 也无法通过长循环得到我需要的东西,内存只会累积并最终陷入困境。退出 R 是最简单的方法。

我不得不经常这样做,所以我写了一个包来帮助我缓存这样的对象:simpleCache

如果你有兴趣尝试,它看起来像这样:

在 R 之外执行此操作:

for (1:lots_of_times) {
Rscript my_script.R
}

然后在 R 中,执行此操作... my_script.R:

library(simpleCache)
simpleCache("very_large_data_table", {r code for how 
you make this table }, assignTo="v_l_d_t_tmp") 

 ### do stuff to v_l_d_t_tmp and output

【讨论】:

  • 如果这是正确的答案,这对 R 来说不是好消息。 simpleCache 看起来很棒,谢谢。
【解决方案2】:

这是一个评论而不是一个答案,但它变得太长了。

我想在这种情况下调用rm 可能是正确的。我认为从第二次迭代开始,如果不调用rm,内存中可能会有3 个表。在复制大对象时,R 无法在复制结束之前释放v_l_d_t_tmp 占用的内存,因为函数调用可能有错误,在这种情况下必须保留旧对象。考虑这个例子:

 x<-1:10
 myfunc<-function(y) {Sys.sleep(3);30}

在这里,我定义了一个对象和一个需要一些时间来做某事的函数。如果你尝试:

 x<-myfunc()

并在它“自然地”结束之前中断执行,对象x 仍然存在,其1:10 内容。因此,我猜在您的情况下,即使您使用相同的符号,R 也无法在复制之前或复制期间释放其内容。如果您在以下副本之前将其删除,则可以。当然,复制后对象会被删除,但在此过程中你可能会耗尽内存。

我绝不是 R 内部的专家,所以不要把我刚才说的当成理所当然。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-24
    • 2019-07-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多