【问题标题】:Why pickle eat memory?为什么泡菜吃内存?
【发布时间】:2012-12-02 00:14:43
【问题描述】:

我试图处理将大量腌制数据分小块写入磁盘的问题。下面是示例代码:

from cPickle import *
from gc import collect

PATH = r'd:\test.dat'
@profile
def func(item):
    for e in item:
        f = open(PATH, 'a', 0)
        f.write(dumps(e))
        f.flush()
        f.close()
        del f
        collect()

if __name__ == '__main__':
    k = [x for x in xrange(9999)]
    func(k)

open() 和 close() 放置在循环内,以排除内存中数据累积的可能原因。

为了说明问题,我附上了使用 Python 3d 派对模块 memory_profiler 获得的内存分析结果:

   Line #    Mem usage  Increment   Line Contents
==============================================
    14                           @profile
    15      9.02 MB    0.00 MB   def func(item):
    16      9.02 MB    0.00 MB       path= r'd:\test.dat'
    17
    18     10.88 MB    1.86 MB       for e in item:
    19     10.88 MB    0.00 MB           f = open(path, 'a', 0)
    20     10.88 MB    0.00 MB           f.write(dumps(e))
    21     10.88 MB    0.00 MB           f.flush()
    22     10.88 MB    0.00 MB           f.close()
    23     10.88 MB    0.00 MB           del f
    24                                   collect()

在循环执行期间,内存使用量出现了奇怪的增长。怎样才能消除?有什么想法吗?

当输入数据量增加时,这些额外数据的量会增长到比输入大得多的大小(更新:在实际任务中,我得到 300+Mb)

还有更广泛的问题 - 存在哪些方法可以在 Python 中正确处理大量 IO 数据?

更新: 我重写了代码,只留下循环体来查看具体何时发生增长,结果如下:

Line #    Mem usage  Increment   Line Contents
==============================================
    14                           @profile
    15      9.00 MB    0.00 MB   def func(item):
    16      9.00 MB    0.00 MB       path= r'd:\test.dat'
    17
    18                               #for e in item:
    19      9.02 MB    0.02 MB       f = open(path, 'a', 0)
    20      9.23 MB    0.21 MB       d = dumps(item)
    21      9.23 MB    0.00 MB       f.write(d)
    22      9.23 MB    0.00 MB       f.flush()
    23      9.23 MB    0.00 MB       f.close()
    24      9.23 MB    0.00 MB       del f
    25      9.23 MB    0.00 MB       collect()

dumps() 似乎会占用内存。 (虽然我实际上认为它会是 write())

【问题讨论】:

  • 首先,您只有 11MB。你确定真的有问题吗?您是否真的尝试过大量数据,看看它是否会线性增长到某个可怕的水平?其次,增量发生在for 循环上(所以大概在item.__next__ 内),而不是dumps 行。 (如果您确实认为这是酸洗,为什么不尝试将dumpswrite 拆分为单独的步骤?)
  • 另外,memory_profiler 说它“通过向操作系统内核查询当前进程分配的内存量来获取内存消耗,这可能与实际分配的内存量略有不同由 Python 解释器使用”。事实上,它可能是方式,方式不同!仅仅因为 Python 调用 free 并不一定意味着平台的分配器会立即将其全部释放给操作系统——事实上,它保留页面映射而不释放它们是完全合理的。
  • 对于您的“广泛问题”:这取决于您所说的大。但是两个基本策略是:不要使用太多(例如,使用 ints 的 numpy 数组而不是 Python 对象的 lists 的名称 list),或者使用数据库(@ 987654337@ 或 sqlite3) 而不是构建一个巨大的内存存储并将其整体保存到磁盘。
  • 查看streaming-pickle,据说它会为你正在做的事情使用更少的内存。
  • @GillBates:你有没有测试过,例如,只是将你的数据存储在shelve 中,看看它是否真的确实以这种方式使用内存,而不是假设它必须因为它使用pickle?此外,您的峰值数据使用量实际上是否超出了您的界限(或者,如果您使用的是 64 位,则将您扔进交换 thrash 地狱)? Python中有一些用例在空间上似乎是线性的,但实际上只是线性到某个恒定限制,之后它们就会变平(通过重用相同的存储)。特别是如果您使用的平台通常不会向内核释放内存并且您是从外部进行测量的。

标签: python performance file-io pickle persistent


【解决方案1】:

Pickle 消耗大量内存,请看这里的解释:http://www.shocksolution.com/2010/01/storing-large-numpy-arrays-on-disk-python-pickle-vs-hdf5adsf/

为什么 Pickle 会消耗这么多内存? 原因是 HDF 是二进制数据管道,而 Pickle 是对象序列化协议。 Pickle 实际上由一个简单的虚拟机 (VM) 组成,它将一个对象转换为一系列操作码并将它们写入磁盘。为了解开某些东西,VM 读取并解释操作码并重建一个对象。这种方法的缺点是 VM 必须在将对象写入磁盘之前在内存中构造对象的完整副本。

Pickle 非常适合小型用例或测试,因为在大多数情况下,内存消耗并不重要。

对于必须转储和加载大量文件和/或大文件的密集工作,您应该考虑使用另一种方式来存储数据(例如:hdf,为您的对象编写自己的序列化/反序列化方法,. ..)

【讨论】:

  • 它是将数据加载到 CPU 内存还是 GPU 内存中? ,它会在转储到文件后立即自行释放吗?我所看到的是,它填满了 GPU 内存并且即使在它被转储后也不会释放内存
  • @TusharSeth 我认为我面临的问题与我提出的问题 today 所强调的问题相同。您是否设法找到解决此问题的方法?
猜你喜欢
  • 2014-03-12
  • 2011-05-04
  • 2012-01-11
  • 2016-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-23
  • 2012-02-01
相关资源
最近更新 更多