在Python中存储大文件的最快方法

时间:2011-10-03 22:55:10

标签: python compression pickle

recently asked a question关于如何将大型python对象保存到文件。我以前遇到过将大量Python字典转换为字符串并通过write()将其写入文件的问题。现在我正在使用泡菜。虽然它有效,但文件非常大(> 5 GB)。我在这么大的文件领域经验不多。我想知道在将这个pickle文件存储到内存之前是否更快,甚至可能将其压缩。

5 个答案:

答案 0 :(得分:10)

您可以使用bzip2压缩数据:

from __future__ import with_statement # Only for Python 2.5
import bz2,json,contextlib

hugeData = {'key': {'x': 1, 'y':2}}
with contextlib.closing(bz2.BZ2File('data.json.bz2', 'wb')) as f:
  json.dump(hugeData, f)

像这样加载:

from __future__ import with_statement # Only for Python 2.5
import bz2,json,contextlib

with contextlib.closing(bz2.BZ2File('data.json.bz2', 'rb')) as f:
  hugeData = json.load(f)

您也可以使用zlibgzip使用几乎相同的界面压缩数据。但是,zlib和gzip的压缩率都低于用bzip2(或lzma)实现的压缩率。

答案 1 :(得分:3)

在实现数据序列化时,Python代码会非常慢。 如果你尝试在纯Python中创建一个与Pickle等价的东西,你会发现它会超级慢。 幸运的是,执行该功能的内置模块非常好。

cPickle外,您会发现marshal模块,速度要快得多。 但它需要一个真正的文件句柄(而不是来自类文件对象)。 你可以import marshal as Pickle看看差异。 我不认为你可以制作比这更快的自定义序列化器...

这是一个实际的(不是那么老)serious benchmark of Python serializers

答案 2 :(得分:1)

  

更快,甚至可能在[写作]之前压缩这个pickle文件

当然有可能,但没有理由在写入之前尝试在内存中创建一个显式的压缩副本(它可能不适合!),当你​​可以自动使其在写入时被压缩,具有内置标准库功能;)

http://docs.python.org/library/gzip.html。基本上,您使用

创建一种特殊的流
gzip.GzipFile("output file name", "wb")

然后使用它就像使用file(或open(...)创建的普通file(...)一样使用它。

答案 3 :(得分:1)

我只是扩展phihag的答案。

当尝试序列化接近RAM大小的对象时,应该避免使用 pickle / cPickle ,因为它requires additional memory of 1-2 times the size of the object是为了序列化。即使将其流式传输到BZ2File也是如此。在我的情况下,我甚至用完了交换空间。

但是JSON的问题(以及与链接文章中提到的HDF文件类似)是它无法序列化元组,这些元组在我的数据中被用作dicts的键。对此没有很好的解决方案;我能找到的最好的方法是将元组转换为字符串,这需要一些自己的内存,但要比pickle少得多。如今,您还可以使用the ujson library,这比json库快得多。

对于由字符串组成的元组(要求字符串不包含逗号):

import ujson as json
from bz2 import BZ2File

bigdata = { ('a','b','c') : 25, ('d','e') : 13 }
bigdata = dict([(','.join(k), v) for k, v in bigdata.viewitems()]) 

f = BZ2File('filename.json.bz2',mode='wb')
json.dump(bigdata,f)
f.close()

重新组合元组:

bigdata = dict([(tuple(k.split(',')),v) for k,v in bigdata.viewitems()])

或者,例如,你的键是2元组的整数:

bigdata2 = { (1,2): 1.2, (2,3): 3.4}
bigdata2 = dict([('%d,%d' % k, v) for k, v in bigdata2.viewitems()])
# ... save, load ...
bigdata2 = dict([(tuple(map(int,k.split(','))),v) for k,v in bigdata2.viewitems()])

这种方法相对于泡菜的另一个优点是,当使用bzip2压缩时,json似乎比泡菜更好地压缩。

答案 4 :(得分:-1)

查看Google的ProtoBuffers。虽然它们并非设计用于开箱即用的大型文件,如音频 - 视频文件,但它们可以很好地处理对象序列化,因为它们是为它设计的。实践表明,有一天您可能需要更新文件的结构,ProtoBuffers将处理它。此外,它们还针对压缩和速度进行了高度优化。而且你并没有依赖Python,Java和C ++得到很好的支持。