web-dev-qa-db-ja.com

cPickleオブジェクトのサイズを小さくする

複数のユーザー定義クラスを含むラージオブジェクトを作成するコードを実行しています。これを後で使用するためにシリアル化する必要があります。私が知ることができることから、酸洗いのみが私の要件に対して十分な用途があります。私はそれらを格納するためにcPickleを使用してきましたが、それが生成するオブジェクトは、500 mbのメモリで実行されるコードから、サイズが約40Gです。シリアル化の速度は問題ではありませんが、オブジェクトのサイズは問題です。ピクルスを小さくするために使用できるヒントや代替プロセスはありますか?

31
ddn

ピクルを使用する必要があり、シリアル化の他の方法が機能しない場合は、ピクルをbzip2を介してパイプすることができます。唯一の問題は、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
38
Viktor Kerkez

CPickle dump呼び出しをzipファイルと組み合わせることができます:

import cPickle
import gzip

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

そして、zipされたピクルスオブジェクトを再ロードするには:

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

より効率的な酸洗いプロトコルを使用したい場合があります。

現在、3つの pickleプロトコル があります。

  • プロトコルバージョン0はオリジナルのASCIIプロトコルであり、以前のバージョンのPythonとの下位互換性があります。
  • プロトコルバージョン1は古いバイナリ形式で、以前のバージョンのPythonとも互換性があります。
  • プロトコルバージョン2はPython 2.3で導入されました。新しいバージョンのクラスのより効率的なpickle化を提供します。

さらに、デフォルトはプロトコル0であり、最も効率の悪いプロトコルです。

プロトコルが指定されていない場合、プロトコル0が使用されます。プロトコルが負の値または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の因数であることを示しています。この要素はこの例に固有のものであることに注意してください。酸洗いするオブジェクトによって結果は異なる場合があります。

2
Moot