Copy.deepcopy против рассола
У меня есть древовидная структура виджетов, например. коллекция содержит модели и модель содержит виджеты. Я хочу скопировать всю коллекцию, copy.deepcopy
быстрее по сравнению с 'pickle и de-pickle'ing объекта, но cPickle как написанный на C намного быстрее, поэтому
- Почему я не должен (мы) всегда использовать cPickle вместо deepcopy?
- Есть ли другая альтернатива? потому что рассол медленнее, чем deepcopy, но cPickle быстрее, так что может быть C-реализация deepcopy будет победителем
Пример тестового кода:
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))
Тайминги:
>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
Ответы
Ответ 1
Проблема заключается в том, что pickle + unpickle может быть быстрее (в реализации C), потому что он менее общий, чем deepcopy: многие объекты могут быть подвергнуты глубокой печати, но не маринованными. Предположим, например, что ваш класс A
был изменен на...:
class A(object):
class B(object): pass
def __init__(self): self.b = self.B()
теперь copy1
все еще работает отлично (сложность замедляет его, но его не останавливает); copy2
и copy3
break, конец трассировки стека говорит...:
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
I.e., травление всегда предполагает, что классы и функции являются объектами верхнего уровня в своих модулях и поэтому разжигает их "по имени" - deepcopying не делает абсолютно никаких таких предположений.
Итак, если у вас есть ситуация, когда скорость "несколько глубокого копирования" абсолютно важна, каждая миллисекунда имеет значение, и вы хотите воспользоваться специальными ограничениями, которые вы ЗНАЕТ, применимы к дублируемым объектам, например которые делают травление применимым, или те, которые предпочитают другие формы, а также сериализации и другие ярлыки, непременно идут дальше, но если вы это сделаете, вы ДОЛЖНЫ знать, что вы ограничиваете свою систему живыми этими ограничениями навсегда и документируете это дизайнерское решение очень четко и четко в интересах будущих сопровождающих.
Для случая NORMAL, где вы хотите общности, используйте deepcopy
! -)
Ответ 2
Вы должны использовать deepcopy, потому что он делает ваш код более читаемым. Использование механизма сериализации для копирования объектов в памяти, по крайней мере, смущает другого разработчика, читающего ваш код. Использование метода deepcopy также означает, что вы можете воспользоваться преимуществами будущих оптимизаций в области глубокой печати.
Первое правило оптимизации: не.
Ответ 3
Еще быстрее было бы избежать копирования в первую очередь. Вы упоминаете, что делаете рендеринг. Зачем нужно копировать объекты?
Ответ 4
Не всегда бывает, что cPickle быстрее, чем deepcopy(). В то время как cPickle, вероятно, всегда быстрее, чем рассол, будь то быстрее, чем глубокая копия зависит от
- размер и уровень вложенности структур, которые нужно скопировать,
- тип содержащихся объектов и
- размер изображения маринованной строки.
Если что-то можно мариновать, оно, очевидно, может быть глубоко скопировано, но противоположное не так: Чтобы рассортировать что-то, он должен быть полностью сериализован; это не так для глубокой печати. В частности, вы можете эффективно реализовать __deepcopy__
, скопировав структуру в памяти (подумайте о типах расширений), не имея возможности сохранить все на диск. (Подумайте о приостановке-о-RAM и приостановлении на диск.)
Известный тип расширения, который удовлетворяет вышеприведенным условиям, может быть ndarray
, и действительно, он служит хорошим контрпримером для вашего наблюдения: с d = numpy.arange(100000000)
ваш код дает разные времена выполнения:
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
Если __deepcopy__
не реализовано, copy
и pickle
обмениваются общей инфраструктурой (см. copy_reg
модуль, обсуждаемый в Отношения между рассолом и глубиной).
Ответ 5
Короткий и несколько поздний:
- Если вы все равно должны cPickle object, вы также можете использовать метод cPickle для глубокой (но документа)
например. Вы можете подумать:
def mydeepcopy(obj):
try:
return cPickle.loads(cPickle.dumps(obj, -1))
except PicklingError:
return deepcopy(obj)