سؤال

لدي بنية شجرة من واجهة المستخدم على سبيل المثال تحتوي المجموعة على نماذج ويحتوي النموذج على أجهزة واجهة مستخدم. أريد نسخ مجموعة كاملة ، copy.deepcopy أسرع بالمقارنة مع "المخلل وإزالة الكائن" ولكن cpickle كما هو مكتوب في C أسرع بكثير ، لذلك

  1. لماذا لا يجب أن أستخدم CPickle دائمًا بدلاً من Deepcopy؟
  2. هل هناك أي نسخة أخرى بديل؟ نظرًا لأن المخلل أبطأ في ذلك الوقت العميق ولكن cpickle أسرع ، لذلك قد يكون تنفيذ C للعمق سيكون الفائز

عينة رمز الاختبار:

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
هل كانت مفيدة؟

المحلول

المشكلة هي أن Pickle+Unpickle يمكن أن يكون أسرع (في تطبيق C) لأنه أقل عمومية من DeepCopy: يمكن أن تكون العديد من الكائنات عميقة ولكن لم يتمخل. افترض على سبيل المثال أن صفك A تم تغييرها إلى ...:

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

حاليا، copy1 لا يزال يعمل بشكل جيد (تعقيد A يبطئه على الانخفاض ولكنه لا يوقفه على الإطلاق) ؛ copy2 و copy3 تقول ، نهاية تتبع المكدس ...:

  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

أي أن التخليل يفترض دائمًا أن الفصول الدراسية والوظائف هي كيانات من المستوى الأعلى في وحداتها ، ولذا فإن المخللات "بالاسم"-لا تجعل Deepcopying أي افتراضات من هذا القبيل على الإطلاق.

لذا ، إذا كان لديك موقف تكون فيه سرعة "النسخ العميق إلى حد ما" أمرًا بالغ الأهمية ، فكل مللي ثانية يهم ، وتريد الاستفادة من القيود الخاصة التي تعرفها تنطبق على الأشياء التي تكررها ، مثل تلك التي تصنع التخليل قابلة للتطبيق ، أو النماذج التي تفضل النماذج الأخرى حتى الآن من التسلسلات وغيرها من الاختصارات ، بالمضي قدمًا بكل الوسائل - ولكن إذا قمت بذلك ، فيجب أن تكون على دراية بأنك تقيد نظامك للعيش من خلال هذه القيود إلى الأبد ، وتوثيق قرار التصميم هذا بوضوح شديد و صراحة لصالح المشرفين في المستقبل.

للحالة العادية ، حيث تريد عمومية ، استخدم deepcopy!-)

نصائح أخرى

يجب أن تستخدم DeepCopy لأنه يجعل الكود الخاص بك أكثر قابلية للقراءة. إن استخدام آلية التسلسل لنسخ الكائنات في الذاكرة هو على الأقل إرباك لمطور آخر يقرأ الكود الخاص بك. يعني استخدام DeepCopy أيضًا أنك تحصل على جني فوائد التحسينات المستقبلية في Deepcopy.

القاعدة الأولى للتحسين: لا.

It is not always the case that cPickle is faster than deepcopy(). While cPickle is probably always faster than pickle, whether it is faster than deepcopy depends on

  • the size and nesting level of the structures to be copied,
  • the type of contained objects, and
  • the size of the pickled string representation.

If something can be pickled, it can obviously be deepcopied, but the opposite is not the case: In order to pickle something, it needs to be fully serialized; this is not the case for deepcopying. In particular, you can implement __deepcopy__ very efficiently by copying a structure in memory (think of extension types), without being able to save everything to disk. (Think of suspend-to-RAM vs. suspend-to-disk.)

A well-known extension type that fulfills the conditions above may be ndarray, and indeed, it serves as a good counterexample to your observation: With d = numpy.arange(100000000), your code gives different runtimes:

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

If __deepcopy__ is not implemented, copy and pickle share common infrastructure (cf. copy_reg module, discussed in Relationship between pickle and deepcopy).

Even faster would be to avoid the copy in the first place. You mention that you are doing rendering. Why does it need to copy objects?

Short and somewhat late:

  • If you have to cPickle an object anyway, you might as well use the cPickle method to deepcopy (but document)

e.g. You might consider:

def mydeepcopy(obj):
    try:
       return cPickle.loads(cPickle.dumps(obj, -1))
    except PicklingError:
       return deepcopy(obj)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top