2009-09-11 20 views
28

Tengo una estructura en árbol de widgets, p. la colección contiene modelos y el modelo contiene widgets. Quiero copiar la colección entera, copy.deepcopy es más rápido en comparación con 'pepinillo y de-pickle'ing el objeto sino cPickle como escrito en C es mucho más rápido, por locopy.deepcopy vs pickle

  1. ¿Por qué no yo (nosotros) siempre estar usando cPickle en lugar de deepcopy?
  2. ¿Hay alguna otra alternativa de copia? porque la salmuera es más lento que DeepCopy pero cPickle es más rápido, por lo que puede ser una aplicación C de deepcopy será el ganador

código de prueba de la muestra:

import copy 
import pickle 
import cPickle 

class A(object): pass 

d = {} 
for i in range(1000): 
    d[i] = A() 

def copy1(): 
    return copy.deepcopy(d) 

def copy2(): 
    return pickle.loads(pickle.dumps(d, -1)) 

def copy3(): 
    return cPickle.loads(cPickle.dumps(d, -1)) 

Tiempos:

>python -m timeit -s "import c" "c.copy1()" 
10 loops, best of 3: 46.3 msec per loop 

>python -m timeit -s "import c" "c.copy2()" 
10 loops, best of 3: 93.3 msec per loop 

>python -m timeit -s "import c" "c.copy3()" 
100 loops, best of 3: 17.1 msec per loop 
+2

Esta es una observación muy útil. – bayer

+0

hm, ¿no debería estar comparando 'pickle' con' copy.copy'? – SilentGhost

+0

¿Depende en absoluto de la estructura de lo que está copiando? Como en, ¿hace una diferencia si, por ejemplo, la memoria asignada a los objetos copiados no es contigua o los punteros subyacentes tienen una larga cadena a seguir? –

Respuesta

29

El problema es que la salmuera + unpickle puede ser más rápido (en la implementación C) porque es menos general de deepcopy: muchos objetos pueden ser deepcopied pero no en escabeche. Supongamos por ejemplo que su clase A se cambiaron a ...:

class A(object): 
    class B(object): pass 
    def __init__(self): self.b = self.B() 

ahora, copy1 todavía funciona bien (La complejidad de un retarda bajadas pero absolutamente no lo detiene); copy2 y copy3 descanso, el extremo de la pila de traza dice ...:

File "./c.py", line 20, in copy3 
    return cPickle.loads(cPickle.dumps(d, -1)) 
PicklingError: Can't pickle <class 'c.B'>: attribute lookup c.B failed 

Es decir, el decapado siempre asume que las clases y funciones son entidades de alto nivel en sus módulos, y así los pepinillos "por su nombre" - - la copia profunda no hace absolutamente ninguna suposición.

Así que si tiene una situación donde la velocidad de "copia profunda" es absolutamente crucial, cada milisegundo importa, Y desea aprovechar las limitaciones especiales que SABE que se aplican a los objetos que está duplicando, como aquellos que hacen que el decapado sea aplicable, o aquellos que favorecen otras formas de serializaciones y otros accesos directos, de todos modos seguir adelante, pero si lo hace DEBE tener en cuenta que está obligando a su sistema a vivir de acuerdo con esas limitaciones para siempre y documentar ese diseño decisión muy clara y explícita en beneficio de los futuros mantenedores.

Para el caso normal, donde desea generalidad, utilizar deepcopy -)

5

Debe utilizar deepcopy porque hace que su código sea más legible. Usar un mecanismo de serialización para copiar objetos en la memoria es, por lo menos, confuso para otro desarrollador que lee su código. El uso de deepcopy también significa que puede obtener los beneficios de optimizaciones futuras en deepcopy.

Primera regla de optimización: no.

+6

Segunda regla de optimización: todavía no – voyager

+2

olvidar las reglas obsoletas, en sustitución de deepcopy por cPickle, hace que mi proyecto de renderizado 25% más rápido y hace feliz a mi cliente :) –

+1

@wds, también i don No creo que sea confuso si la función de envoltura para copiar el objeto se llama DeepcopyObject con un buen comentario –

1

Aún más rápido sería evitar la copia en el primer lugar. Mencionas que estás haciendo renderizado. ¿Por qué necesita copiar objetos?

+1

sí, idealmente no necesitaría copiar, ya que la vista (representación) y el modelo serán desacoplados, pero en mi caso, la representación modifica el modelo, por lo tanto, necesito copiar el modelo antes de representarlo, así el original no se modifica –

+1

Yo don No es para vencer a un caballo muerto, pero arreglar el problema donde renderizar modifica el modelo te hará muy feliz. –

+0

Acepto, pero sería una tarea costosa cambiar tanto código, ya que la discusión aquí no es posible He añadido una pregunta http://stackoverflow.com/questions/1414246/how-to-decouple-model-view- for-widgets –

0

corto y un poco tarde:

  • Si tiene que cPickle un objeto de todos modos, que también podría utilizar el método cPickle a DeepCopy (pero documento)

por ejemplo, Usted podría considerar:

def mydeepcopy(obj): 
    try: 
     return cPickle.loads(cPickle.dumps(obj, -1)) 
    except PicklingError: 
     return deepcopy(obj) 
+0

¿por qué desaparecen mis líneas nuevas, error en stov? – Lars

+0

pero esa no era la pregunta, yo ya estaba usando cPickle de la misma manera –

+1

La forma más rápida que pude pensar, además de escribir tu propia extensión python c – Lars

1

Es no siempre es el caso que cPickle es más rápido que deepcopy(). Mientras cPickle es probablemente siempre más rápido que la salmuera, si es más rápido que deepcopy depende de

  • el tamaño y el nivel de anidamiento de las estructuras a ser copiado,
  • el tipo de objetos que contiene, y
  • el tamaño de la representación de cuerda en escabeche.

Si algo puede ser decapada, es evidentemente una afirmación deepcopied, pero lo contrario no es el caso: Con el fin de conservar en vinagre algo, tiene que ser totalmente serializado; este no es el caso para la copia profunda. En particular, puede implementar __deepcopy__ muy eficientemente al copiar una estructura en la memoria (piense en los tipos de extensión), sin poder guardar todo en el disco. (Piense en suspender-a-RAM vs. suspender-a-disco.)

Un tipo de extensión conocido que cumpla las condiciones anteriores puede ser ndarray, y de hecho, sirve como un buen contraejemplo para su observación: d = numpy.arange(100000000), su código da diferentes tiempos de ejecución:

In [1]: import copy, pickle, cPickle, numpy 

In [2]: d = numpy.arange(100000000) 

In [3]: %timeit pickle.loads(pickle.dumps(d, -1)) 
1 loops, best of 3: 2.95 s per loop 

In [4]: %timeit cPickle.loads(cPickle.dumps(d, -1)) 
1 loops, best of 3: 2.37 s per loop 

In [5]: %timeit copy.deepcopy(d) 
1 loops, best of 3: 459 ms per loop 

Si __deepcopy__ no se ha implementado, copy e infraestructura pickle acción ordinaria (cf. módulo copy_reg, discutido en Relationship between pickle and deepcopy).

Cuestiones relacionadas