J'exécute du code qui crée de gros objets, contenant plusieurs classes définies par l'utilisateur, que je dois ensuite sérialiser pour une utilisation ultérieure. D'après ce que je peux dire, seul le décapage est suffisamment polyvalent pour mes besoins. J'utilise cPickle pour les stocker, mais les objets qu'il génère mesurent environ 40 Go, à partir d'un code qui s'exécute dans 500 Mo de mémoire. La vitesse de sérialisation n'est pas un problème, mais la taille de l'objet l'est. Y a-t-il des conseils ou des procédés alternatifs que je peux utiliser pour réduire la taille des cornichons?
Si vous devez utiliser du cornichon et qu'aucune autre méthode de sérialisation ne fonctionne pour vous, vous pouvez toujours diriger le cornichon par bzip2
. Le seul problème est que bzip2
est un peu lent ... gzip
devrait être plus rapide, mais la taille du fichier est presque 2x plus grande:
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
Nous voyons donc que la taille du fichier de bzip2
est presque 40 fois plus petit, gzip
est 20 fois plus petit. Et gzip est assez proche des performances du cPickle brut, comme vous pouvez le voir:
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
Vous pouvez combiner votre appel cPickle dump
avec un fichier 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)
Et pour recharger un objet mariné zippé:
def load_zipped_pickle(filename):
with gzip.open(filename, 'rb') as f:
loaded_object = cPickle.load(f)
return loaded_object
Vous voudrez peut-être utiliser un protocole de décapage plus efficace.
À l'heure actuelle, il existe trois protocoles de pickle :
- La version 0 du protocole est le protocole ASCII et est rétrocompatible avec les versions antérieures de Python.
- Le protocole version 1 est l'ancien format binaire qui est également compatible avec les versions antérieures de Python.
- Le protocole version 2 a été introduit dans Python 2.3. Il fournit un décapage beaucoup plus efficace des classes de nouveau style.
et de plus, la valeur par défaut est le protocole 0, le moins efficace:
Si aucun protocole n'est spécifié, le protocole 0 est utilisé. Si le protocole est spécifié comme une valeur négative ou HIGHEST_PROTOCOL, la version de protocole la plus élevée disponible sera utilisée.
Vérifions la différence de taille entre l'utilisation du dernier protocole, qui est actuellement le protocole 2 (le plus efficace) et l'utilisation du protocole 0 (par défaut) pour un exemple arbitraire. Notez que j'utilise protocol = -1 ici, pour nous assurer que nous utilisons toujours le dernier protocole, et que j'importe cPickle pour nous assurer que nous utilisons l'implémentation C plus rapide:
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)
L'impression que je reçois est:
2172.27246094 781.703125 2.77889698975
Indiquant que le décapage utilisant l'ancien protocole a utilisé 2172 Ko, le décapage utilisant le nouveau protocole a utilisé 782 Ko et la différence est un facteur x2,8. Notez que ce facteur est spécifique à cet exemple - vos résultats peuvent varier en fonction de l'objet que vous décapez.