2011-12-15 26 views
18

Estoy implementando un programa que necesita serializar y deserializar objetos grandes, así que estaba haciendo algunas pruebas con los módulos pickle, cPickle y marshal para elegir el mejor módulo. En el camino encontré algo muy interesante:mariscal vuelca más rápido, cPickle carga más rápido

Estoy usando dumps y luego loads (para cada módulo) en una lista de dicts, tuples, ints, float y strings.

Ésta es la salida de mi punto de referencia:

DUMPING a list of length 7340032 
---------------------------------------------------------------------- 
pickle => 14.675 seconds 
length of pickle serialized string: 31457430 

cPickle => 2.619 seconds 
length of cPickle serialized string: 31457457 

marshal => 0.991 seconds 
length of marshal serialized string: 117440540 

LOADING a list of length: 7340032 
---------------------------------------------------------------------- 
pickle => 13.768 seconds 
(same length?) 7340032 == 7340032 

cPickle => 2.038 seconds 
(same length?) 7340032 == 7340032 

marshal => 6.378 seconds 
(same length?) 7340032 == 7340032 

Así, a partir de estos resultados se puede ver que marshal era extremadamente rápido en el vertido parte del índice de referencia:

14.8x veces más rápido que pickle y 2.6 veces más rápido que cPickle.

Pero, para mi gran sorpresa, marshal era, con mucho, más lento que en el cPickle parte carga:

2.2x veces más rápido que pickle, pero 3.1x veces más lento que cPickle.

Y en cuanto a memoria RAM, mientras que el rendimiento marshalcarga también fue muy ineficiente:

Ubuntu System Monitor

Supongo que la razón por la carga con marshal es tan lento de alguna manera está relacionado con la longitud de su serie serializada (mucho más larga que pickle y cPickle).

  • ¿Por qué marshal volcamientos más rápidos y más lentos?
  • ¿Por qué marshal serie serializada es tan larga?
  • ¿Por qué la carga de marshal es tan ineficiente en la RAM?
  • ¿Hay alguna forma de mejorar el rendimiento de carga de marshal?
  • ¿Hay alguna forma de combinar marshal volcado rápido con cPickle cargando rápidamente?
+0

downvoter, importar para compartir? – juliomalegria

+3

Tu pregunta es un callejón sin salida. El módulo 'mariscal' no está destinado a ser utilizado como una alternativa a' pickle'. No hay documentación oficial para el formato de archivo Marshal y podría cambiar de una versión a otra, por lo que los resultados de su evaluación comparativa podrían ser falsos en el futuro. –

+0

En cuanto a las diferencias de velocidad: sospecho que todo se trata del archivo IO: el archivo producido por el jefe de policía es casi cuatro veces más grande (112 MB frente a 30 MB). –

Respuesta

18

cPickle tiene un algoritmo más inteligente que marshal y es capaz de hacer trucos para reducir el espacio utilizado por objetos grandes. Eso significa que será más lento de decodificar pero más rápido de codificar ya que la salida resultante es más pequeña. marshal es simplista y serializa el objeto directamente tal cual, sin analizarlo más. Eso también responde por qué la carga de marshal es tan ineficiente, simplemente tiene que hacer más trabajo, como leer más datos del disco, para poder hacer lo mismo que cPickle.

marshal y cPickle al final, realmente no se puede obtener un ahorro rápido ni una carga rápida, ya que un ahorro rápido implica analizar menos las estructuras de datos, lo que implica guardar una gran cantidad de datos en el disco.

En cuanto al hecho de que marshal puede ser incompatible con otras versiones de Python, se debe utilizar generalmente cPickle:

"Esto no es un general‘módulo de persistencia’para la persistencia general y transferencia de objetos de Python a través. Llamadas RPC, ver los módulos pickle y shelve. El módulo Marshal existe principalmente para admitir la lectura y escritura del código "pseudo compilado" para módulos Python de archivos .pyc. Por lo tanto, los desarrolladores de Python se reservan el derecho de modificar el formato de Marshal hacia atrás formas incompatibles si surge la necesidad. Si está serializando y deserializando objetos Python, use el módulo pickle en su lugar - el rendimiento es c omparable, la independencia de la versión está garantizada, y pickle admite una gama de objetos sustancialmente más amplia que Marshal ". (the python docs about marshal)

3

Como se puede ver, la salida producida por cPickle.dump tiene aproximadamente 1/4 de la longitud de la salida producida por marshal.dump. Esto significa que cPickle debe usar un algoritmo más complicado para volcar los datos ya que se eliminan cosas innecesarias. Al cargar la lista de volcado, marshal tiene que trabajar con muchos más datos, mientras que cPickle puede procesar sus datos rápidamente ya que hay menos datos que deben analizarse.

En cuanto al hecho de que marshal puede ser incompatible con otras versiones de Python, se debe utilizar generalmente cPickle:

"Esto no es un general‘módulo de persistencia’para la persistencia general y transferencia de objetos de Python a través. Llamadas RPC, ver los módulos pickle y shelve. El módulo Marshal existe principalmente para admitir la lectura y escritura del código "pseudo compilado" para módulos Python de archivos .pyc. Por lo tanto, los desarrolladores de Python se reservan el derecho de modificar el formato de Marshal hacia atrás formas incompatibles si surge la necesidad. Si está serializando y deserializando objetos Python, use el módulo pickle en su lugar - el rendimiento es c omparable, la independencia de la versión está garantizada, y pickle admite una gama de objetos sustancialmente más amplia que Marshal ". (the python docs about marshal)

9

La diferencia entre estos puntos de referencia da una idea para acelerar cPickle:

Input: ["This is a string of 33 characters" for _ in xrange(1000000)] 
cPickle dumps 0.199 s loads 0.099 s 2002041 bytes 
marshal dumps 0.368 s loads 0.138 s 38000005 bytes 

Input: ["This is a string of 33 "+"characters" for _ in xrange(1000000)] 
cPickle dumps 1.374 s loads 0.550 s 40001244 bytes 
marshal dumps 0.361 s loads 0.141 s 38000005 bytes 

En el primer caso, la lista se repite la misma cadena. La segunda lista es equivalente, pero cada cadena es un objeto separado, porque es el resultado de una expresión. Ahora, si originalmente está leyendo sus datos desde una fuente externa, podría considerar algún tipo de deduplicación de cadenas.

11

Algunas personas pueden pensar que esto es demasiado pirateo, pero he tenido un gran éxito al simplemente completar las llamadas al volcado de pickle con gc.disable() y gc.enable(). Por ejemplo, las tijeras de los siguientes escribiendo una lista de ~ 50 MB de diccionarios va de 78 segundos a 4.

# not a complete example.... 
gc.disable() 
cPickle.dump(params,fout,cPickle.HIGHEST_PROTOCOL)   
fout.close()    
gc.enable() 
+2

Guau, esto realmente funciona ... pero ¿cuáles son las repercusiones? – tdc

+0

¡Esto funciona perfectamente! El tiempo total requerido se redujo en 20 veces para mí también. Aunque @Chris, ¿puedes señalarnos las repercusiones (si las hay) de las mismas? –

+0

@tdc, Tejas, ya no podrás volcar objeto acíclico, p. 'x' en' x = []; x.append (x) 'causará un ValueError si Pickler.fast está habilitado. – kay

5

Puede hacer cca cPickle. 50x (!) Más rápido creando una instancia de cPickle.Pickler, y luego poner la opción indocumentado 'rápido' a 1:

outfile = open('outfile.pickle') 
fastPickler = cPickle.Pickler(outfile, cPickle.HIGHEST_PROTOCOL) 
fastPickler.fast = 1 
fastPickler.dump(myHugeObject) 
outfile.close() 

Pero si su myHugeObject tiene referencias cíclicas, el método de vaciado no tendrá fin.

+0

¡Es útil saber! ¿Hace 'carga 'más rápido también? – juliomalegria

+0

No lo creo, la opción rápida solo desactiva la detección de subobjetos de duplicidad cuando se decapa el dato. Puede encontrar más información en la documentación de la serie python 3 (http://docs.python.org/3/library/pickle.html?highlight=pickle#pickle.Pickler.fast) o, por supuesto, en el código –

3

Puede mejorar la eficacia del almacenamiento al comprimir el resultado de la serialización.

Mi corazonada es que comprimir datos y alimentarlos en la deserialización sería más rápido que leer datos sin procesar desde el disco duro.

La prueba a continuación se realizó para demostrar que la compresión aceleraría el proceso de deserialización. El resultado no fue el esperado ya que la máquina estaba equipada con SSD. En HHD equipar la máquina, comprimir los datos usando lz4 sería más rápido ya que la lectura del disco promedio oscila entre 60-70mb/s.

LZ4: Con una reducción de velocidad del 18%, la compresión produce un 77,6% de almacenamiento adicional.

marshal - compression speed time 
Bz2 7.492605924606323 10363490 
Lz4 1.3733329772949219 46018121 
--- 1.126852035522461 205618472 
cPickle - compression speed time 
Bz2 15.488649845123291 10650522 
Lz4 9.192650079727173 55388264 
--- 8.839831113815308 204340701 
+0

¡Resultados interesantes! ¿Estás insinuando que de alguna manera evitaste tener que descomprimir los datos antes de deserializar? ¿Si es así, cómo? – seaotternerd

Cuestiones relacionadas