2009-09-11 28 views
28

Tôi có cấu trúc cây của tiện ích, ví dụ: bộ sưu tập chứa các mô hình và mô hình chứa các tiện ích con. Tôi muốn sao chép toàn bộ bộ sưu tập, copy.deepcopy là nhanh hơn so với 'dưa và de-pickle'ing đối tượng nhưng cPickle như được viết bằng C là nhanh hơn nhiều, vì vậycopy.deepcopy vs pickle

  1. Tại sao tôi (chúng tôi) không nên luôn luôn đang sử dụng cPickle thay vì sâu?
  2. Có phương án sao chép nào khác không? vì dưa là chậm hơn sau đó deepcopy nhưng cPickle là nhanh hơn, do đó có thể là một thực hiện C deepcopy sẽ là người chiến thắng

mã kiểm tra mẫu:

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)) 

Thời gian:

>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

Đây là một quan sát rất hữu ích. – bayer

+0

hm, bạn có nên so sánh 'pickle' với' copy.copy' không? – SilentGhost

+0

Nó có phụ thuộc vào cấu trúc của những gì bạn đang sao chép không? Như trong, liệu nó có tạo ra sự khác biệt nếu, giả sử, bộ nhớ được phân bổ cho các đối tượng được sao chép là không liền kề hoặc các con trỏ bên dưới có một chuỗi dài để làm theo? –

Trả lời

29

Vấn đề là, dưa + unpickle có thể nhanh hơn (trong việc thực hiện C) vì nó ít chung hơn deepcopy: nhiều đối tượng có thể được deepcopied nhưng không ngâm. Giả sử ví dụ rằng lớp học của bạn A đã được thay đổi để ...:

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

bây giờ, copy1 vẫn hoạt động tốt (A của sự phức tạp làm chậm nó trầm nhưng hoàn toàn không ngăn chặn nó); copy2copy3 phá vỡ, cuối stack trace nói ...:

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 

Ie, tẩy luôn giả định rằng các lớp học và chức năng là các đơn vị cấp cao nhất trong các module của họ, và vì vậy dưa chua họ "theo tên" - - sâu sắc làm cho hoàn toàn không có giả định như vậy. Vì vậy, nếu bạn có một tình huống mà tốc độ "phần nào sao chép sâu" là cực kỳ quan trọng, mỗi mili giây vấn đề, VÀ bạn muốn tận dụng những hạn chế đặc biệt mà bạn BIẾT áp dụng cho các đối tượng bạn đang nhân bản, chẳng hạn như nhưng nếu bạn làm bạn PHẢI nhận thức được rằng bạn đang hạn chế hệ thống của bạn sống theo những giới hạn đó mãi mãi và ghi lại thiết kế đó quyết định rất rõ ràng và rõ ràng vì lợi ích của các nhà bảo trì trong tương lai.

Đối với trường hợp NORMAL, nơi bạn muốn tính tổng quát, sử dụng deepcopy -!)

5

Bạn nên sử dụng bản in sâu vì nó làm cho mã của bạn dễ đọc hơn. Sử dụng cơ chế tuần tự hóa để sao chép các đối tượng trong bộ nhớ ít nhất là gây nhầm lẫn cho một nhà phát triển khác đang đọc mã của bạn. Sử dụng deepcopy cũng có nghĩa là bạn có được gặt hái những lợi ích của việc tối ưu hóa trong tương lai trong deepcopy.

Quy tắc tối ưu hóa đầu tiên: không.

+6

Nguyên tắc tối ưu hóa thứ hai: Chưa – voyager

+2

Hãy quên đi các quy tắc đã lỗi thời, thay thế sâu bằng cpickle, làm cho dự án của tôi hiển thị nhanh hơn 25% và làm cho khách hàng của tôi hạnh phúc :) –

+1

@wds, cũng i don không nghĩ rằng nó sẽ gây nhầm lẫn nếu chức năng wrapper để sao chép đối tượng được đặt tên deepcopyObject với một bình luận tốt –

1

Thậm chí nhanh hơn sẽ là để tránh sự sao chép ở nơi đầu tiên. Bạn đề cập đến rằng bạn đang làm rendering. Tại sao nó cần phải sao chép các đối tượng?

+1

có lý tưởng nó sẽ không cần sao chép, như xem (rendering) và mô hình sẽ được de-coupled nhưng trong trường hợp của tôi, rendering không sửa đổi các mô hình do đó tôi cần phải sao chép mô hình trước khi rendering như vậy ban đầu không được sửa đổi –

+1

tôi don không có nghĩa là để đánh bại một con ngựa chết, nhưng sửa chữa vấn đề nơi dựng hình sửa đổi mô hình sẽ làm cho bạn rất hạnh phúc. –

+0

Tôi đồng ý nhưng sẽ là một việc tốn kém để thay đổi quá nhiều mã, vì không thể thảo luận ở đây tôi đã thêm câu hỏi http://stackoverflow.com/questions/1414246/how-to-decouple-model-view- for-widgets –

0

ngắn và hơi muộn:

  • Nếu bạn phải cPickle một đối tượng dù sao, bạn cũng có thể sử dụng phương pháp cPickle để deepcopy (nhưng tài liệu)

ví dụ Bạn có thể xem xét:

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

tại sao dòng mới của tôi biến mất, lỗi trong bếp? – Lars

+0

nhưng đó không phải là câu hỏi, tôi đã sử dụng cPickle theo cách tương tự –

+1

Cách nhanh nhất tôi có thể nghĩ đến, ngoài việc viết phần mở rộng python c của riêng bạn – Lars

1

Đó là không luôn luôn như vậy mà cPickle nhanh hơn deepcopy(). Trong khi cPickle có lẽ là luôn nhanh hơn so với dưa, cho dù đó là nhanh hơn so với deepcopy phụ thuộc vào

  • kích thước và mức độ làm tổ của các cấu trúc được sao chép,
  • loại vật chứa, và
  • kích thước của biểu diễn chuỗi ngâm.

Nếu một cái gì đó có thể được ngâm, nó có thể rõ ràng được deepcopied, nhưng điều ngược lại không phải như vậy: Để dưa cái gì đó, nó cần phải được hoàn toàn serialized; đây không phải là trường hợp cho việc in sâu. Cụ thể, bạn có thể thực hiện __deepcopy__ rất hiệu quả bằng cách sao chép cấu trúc trong bộ nhớ (nghĩ về các loại tiện ích mở rộng), mà không thể lưu mọi thứ vào đĩa. (Hãy suy nghĩ về việc tạm ngừng RAM so với tạm ngừng.)

Loại tiện ích mở rộng nổi tiếng đáp ứng các điều kiện trên có thể là ndarray và thực sự là một phản hồi tốt cho quan sát của bạn: d = numpy.arange(100000000), mã của bạn mang đến cho runtimes khác nhau:

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 

Nếu __deepcopy__ không được thực hiện, copy và cơ sở hạ tầng pickle cổ phần phổ thông (cf. copy_reg module, được thảo luận trong Relationship between pickle and deepcopy).

Các vấn đề liên quan