减小cPickle对象的大小

时间:2013-08-27 20:27:41

标签: python serialization pickle

我正在运行创建大型对象的代码,其中包含多个用户定义的类,然后我必须将其序列化以供以后使用。据我所知,只有酸洗才能满足我的要求。我一直在使用cPickle存储它们,但它生成的对象大小约为40G,代码运行在500 MB的内存中。序列化的速度不是问题,但对象的大小是。是否有任何提示或替代流程可以使泡菜变小?

3 个答案:

答案 0 :(得分:37)

您可以将cPickle dump调用与zipfile结合使用:

import cPickle
import gzip

def save_zipped_pickle(obj, filename, protocol=-1):
    with gzip.open(filename, 'wb') as f:
        cPickle.dump(obj, f, protocol)

重新加载压缩的腌制物品:

def load_zipped_pickle(filename):
    with gzip.open(filename, 'rb') as f:
        loaded_object = cPickle.load(f)
        return loaded_object

答案 1 :(得分:37)

如果您必须使用pickle,并且没有其他序列化方法适合您,您可以随时通过bzip2管道pickle。唯一的问题是bzip2有点慢...... gzip应该更快,但文件大小几乎要大2倍:

In [1]: class Test(object):
            def __init__(self):
                self.x = 3841984789317471348934788731984731749374
                self.y = 'kdjsaflkjda;sjfkdjsf;klsdjakfjdafjdskfl;adsjfl;dasjf;ljfdlf'
        l = [Test() for i in range(1000000)]

In [2]: import cPickle as pickle          
        with open('test.pickle', 'wb') as f:
            pickle.dump(l, f)
        !ls -lh test.pickle
-rw-r--r--  1 viktor  staff    88M Aug 27 22:45 test.pickle

In [3]: import bz2
        import cPickle as pickle
        with bz2.BZ2File('test.pbz2', 'w') as f:
            pickle.dump(l, f)
        !ls -lh test.pbz2
-rw-r--r--  1 viktor  staff   2.3M Aug 27 22:47 test.pbz2

In [4]: import gzip
        import cPickle as pickle
        with gzip.GzipFile('test.pgz', 'w') as f:
            pickle.dump(l, f)
        !ls -lh test.pgz
-rw-r--r--  1 viktor  staff   4.8M Aug 27 22:51 test.pgz

因此,我们发现bzip2的文件大小几乎缩小了40倍,gzip小了20倍。 gzip与原始cPickle的性能非常接近,你可以看到:

cPickle : best of 3: 18.9 s per loop
bzip2   : best of 3: 54.6 s per loop
gzip    : best of 3: 24.4 s per loop

答案 2 :(得分:2)

您可能希望使用更有效的酸洗协议。

截至目前,有三个pickle protocols

  
      
  • 协议版本0是原始的ASCII协议,向后兼容早期版本的Python。
  •   
  • 协议版本1是旧的二进制格式,它也与早期版本的Python兼容。
  •   
  • 在Python 2.3中引入了协议版本2。它提供了更有效的新式类型的酸洗。
  •   

此外,默认为协议0,效率最低的是:

  

如果未指定协议,则使用协议0。如果protocol被指定为负值或HIGHEST_PROTOCOL,则将使用可用的最高协议版本。

让我们检查使用最新协议(当前是协议2(最有效的协议)和使用协议0(默认))的任意示例之间的大小差异。请注意,我在这里使用protocol = -1,以确保我们始终使用最新的协议,并且我导入cPickle以确保我们使用更快的C实现:

import numpy
from sys import getsizeof
import cPickle as pickle

# Create list of 10 arrays of size 100x100
a = [numpy.random.random((100, 100)) for _ in xrange(10)]

# Pickle to a string in two ways
str_old = pickle.dumps(a, protocol=0)
str_new = pickle.dumps(a, protocol=-1)

# Measure size of strings
size_old = getsizeof(str_old)
size_new = getsizeof(str_new)

# Print size (in kilobytes) using old, using new, and the ratio
print size_old / 1024.0, size_new / 1024.0, size_old / float(size_new)

我得到的打印出来是:

2172.27246094 781.703125 2.77889698975

表明使用旧协议进行的酸洗使用了2172KB,使用新协议的酸洗耗尽了782KB,差异是x2.8。请注意,此因子特定于此示例 - 您的结果可能会有所不同,具体取决于您正在腌制的对象。