【问题标题】:MemoryError with Pickle in PythonPython中Pickle的MemoryError
【发布时间】:2015-03-20 01:40:55
【问题描述】:

我正在处理一些数据,并将结果存储在三个字典中,并使用 Pickle 将它们保存到磁盘。每个字典有 500-1000MB。

现在我正在加载它们:

import pickle
with open('dict1.txt', "rb") as myFile:
    dict1 = pickle.load(myFile)

但是,我已经在加载我得到的第一本字典了:

*** set a breakpoint in malloc_error_break to debug
python(3716,0xa08ed1d4) malloc: *** mach_vm_map(size=1048576) failed (error code=3)
*** error: can't allocate region securely
*** set a breakpoint in malloc_error_break to debug
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 858, in load
    dispatch[key](self)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1019, in load_empty_dictionary
    self.stack.append({})
MemoryError

如何解决这个问题?我的电脑有 16GB 的 RAM,所以我发现加载 800MB 的字典崩溃是很不寻常的。我还发现不寻常的是保存字典时没有问题。

此外,我计划在未来处理更多数据,从而产生更大的字典(磁盘上 3-4GB),因此我们非常感谢任何关于如何提高效率的建议。

【问题讨论】:

  • 您使用的是什么操作系统? 磁盘上文件的大小还是你测量的实际内存使用量?
  • 这取决于您的操作系统允许进程分配多少内存。
  • Size 是磁盘上的文件大小。我正在使用 Mac OS 10.10。有没有办法调整允许分配多少内存?
  • 800MB 的数据不会转化为 800MB 的内存使用;它可能更大,也可能更小,但通常更大。您最初是如何制作这些泡菜的?
  • 那么你的dict1 有多大?您必须递归使用sys.getsizeof() 来获取该对象的内存占用。该占用空间取决于操作系统,以及您使用的是 32 位还是 64 位进程。

标签: python memory dictionary memory-leaks pickle


【解决方案1】:

如果字典中的数据是numpy 数组,则有一些包(例如joblibklepto)可以有效地对大型数组进行酸洗,因为kleptojoblib 都了解如何使用numpy.array 的最小状态表示。如果您没有 array 数据,我的建议是使用 klepto 将字典条目存储在多个文件(而不是单个文件)或数据库中。

请参阅我对一个非常相关的问题https://stackoverflow.com/a/25244747/2379433 的回答,如果您可以对多个文件而不是单个文件进行酸洗,希望并行保存/加载您的数据,或者希望轻松尝试存储格式和后端,看看哪种最适合您的情况。另请参阅:https://stackoverflow.com/a/21948720/2379433 以了解其他潜在的改进,以及:https://stackoverflow.com/a/24471659/2379433

正如上面的链接所讨论的,您可以使用klepto——它使您能够使用通用 API 轻松地将字典存储到磁盘或数据库中。 klepto 还使您能够选择存储格式(picklejson 等)——HDF5(或 SQL 数据库)也是另一个不错的选择,因为它允许并行访问。 klepto 可以使用专门的 pickle 格式(如 numpy's)和压缩(如果您关心大小而不是访问数据的速度)。

klepto 让您可以选择使用“all-in-one”文件或“one-entry-per”文件存储字典,还可以利用多处理或多线程——这意味着您可以保存和加载字典并行进出后端的项目。例如,请参阅上面的链接。

【讨论】:

  • 感谢您的回答。我保留了 pickle,但我从根本上修改了我的代码以生成占用空间小得多的 numpy 数组。现在它工作正常。
  • @Mike 我正在使用 mutliprocessing.Pool 和 Pandas。在apply_async 之后,如果 pandas 数据帧有点大,它会抛出 MemoryError。我可以使用klepto 来缓解这种情况吗?
  • @MSS:如果没有更多细节,很难从您的问题中分辨出来。可能。 Klepto 可以将数据推送到磁盘上,从而超出内存,并为您提供一次访问部分数据的接口。根据您的用例,我希望 kleptodask 可能会有所帮助。
【解决方案2】:

这是泡菜的固有问题, 这是用于与相当少量的 数据。字典的大小,当加载到内存中时, 比磁盘大很多倍。

加载 100MB 的 pickle 文件后,您可能会 一本将近 1GB 左右的字典。 网上有一些公式可以计算开销,但我只能推荐使用一些 对于如此大量的数据,像 MySQL 或 PostgreSQL 这样的体面数据库。

【讨论】:

  • 是啊...我知道大小不相等,但没想到这个比例可能是10x...
【解决方案3】:

我假设你使用 32 位 Python,它有 4GB 的限制。您应该使用 64 位而不是 32 位。我试过了,我的腌制字典超过了 1.7GB,除了时间变长,我没有遇到任何问题。

【讨论】:

  • 您能否进一步澄清您的答案?如何?代码 sn-p ?
  • 当我尝试使用 Python 32 位版本加载超过 1.7GB 的腌制 dict 数据时,它被挂了很久很久。但是 64 位 Python 版本不会。
猜你喜欢
  • 1970-01-01
  • 2011-04-28
  • 1970-01-01
  • 1970-01-01
  • 2018-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-09
相关资源
最近更新 更多