2013-08-13 33 views
6

Tôi có một đối tượng đệm trong C++ kế thừa từ std::vector<char>. Tôi muốn chuyển đổi bộ đệm này thành một chuỗi Python để tôi có thể gửi nó qua mạng thông qua giao thức protocol.transport.write của Twisted.Tốc độ sao chép bộ đệm Python - tại sao mảng chậm hơn chuỗi?

Hai cách tôi nghĩ để làm điều này là (1) thực hiện một chuỗi và điền nó char bởi char:

def scpychar(buf, n): 
    s = '' 
    for i in xrange(0, n): 
     s += buf[i] 
    return s 

và (2) thực hiện một mảng char (kể từ khi tôi biết làm thế nào lớn bộ đệm là) , điền nó và chuyển đổi nó thành một chuỗi

def scpyarr(buf, n): 
    a = array.array('c','0'*n) 
    for i in xrange(0, n): 
     a[i] = buf[i] 
    return a.tostring() 

tôi đã có thể nghĩ rằng (1) có để làm cho một đối tượng chuỗi mới mỗi khi s += buf[i] được gọi, và sao chép nội dung của chuỗi cũ. Vì vậy, tôi đã mong đợi (2) để được nhanh hơn (1). Nhưng nếu tôi kiểm tra điều này bằng cách sử dụng timeit, tôi thấy rằng (1) thực sự là nhanh gấp đôi (2).

Tôi đã tự hỏi liệu có ai đó có thể giải thích tại sao (1) nhanh hơn không?

Điểm thưởng cho một cách hiệu quả hơn để chuyển đổi từ std::vector<char> thành chuỗi Python.

Trả lời

2

CPython đôi khi có thể tối ưu hóa chuỗi += để được đặt tại vị trí nếu có thể xác định rằng không có ai giữ tham chiếu đến chuỗi cũ. Thuật toán (1) có thể kích hoạt tối ưu hóa, do đó, nó không bị chạy bậc hai nếu không nó sẽ có. Tuy nhiên, hành vi này không được đảm bảo và các triển khai Python khác có thể không hỗ trợ nó.

Hãy thử

''.join(buf) 

Nó sẽ cung cấp hiệu suất tuyến tính thời gian thực trên bất kỳ thực hiện Python, không giống như (1), và nhanh hơn (2).

+0

Tôi không nghĩ rằng tham gia sẽ chấp nhận các vector, nhưng nó. Mát mẻ! – Corey

+0

thú vị, bạn có tham khảo về sửa đổi tại chỗ cpython không? –

+0

@RyanHaining: Xem chú thích 6 [ở đây] (http://docs.python.org/2/library/stdtypes.html#sequence-types-str-unicode-list-tuple-bytearray-buffer-xrange). – user2357112

-1

nhập dis và xem dis.dis (scpyarr) và dis.dis (scpychar). scpychar có ít hoạt động thông dịch hơn.

>>> import dis 
>>> def scpyarr(buf, n): 
...  a = array.array('c','0'*n) 
...  for i in xrange(0, n): 
...   a[i] = buf[i] 
...  return a.tostring() 
... 
>>> dis.dis(scpyarr) 
    2   0 LOAD_GLOBAL    0 (array) 
       3 LOAD_ATTR    0 (array) 
       6 LOAD_CONST    1 ('c') 
       9 LOAD_CONST    2 ('0') 
      12 LOAD_FAST    1 (n) 
      15 BINARY_MULTIPLY  
      16 CALL_FUNCTION   2 
      19 STORE_FAST    2 (a) 

    3   22 SETUP_LOOP    37 (to 62) 
      25 LOAD_GLOBAL    1 (xrange) 
      28 LOAD_CONST    3 (0) 
      31 LOAD_FAST    1 (n) 
      34 CALL_FUNCTION   2 
      37 GET_ITER    
     >> 38 FOR_ITER    20 (to 61) 
      41 STORE_FAST    3 (i) 

    4   44 LOAD_FAST    0 (buf) 
      47 LOAD_FAST    3 (i) 
      50 BINARY_SUBSCR  
      51 LOAD_FAST    2 (a) 
      54 LOAD_FAST    3 (i) 
      57 STORE_SUBSCR   
      58 JUMP_ABSOLUTE   38 
     >> 61 POP_BLOCK   

    5  >> 62 LOAD_FAST    2 (a) 
      65 LOAD_ATTR    2 (tostring) 
      68 CALL_FUNCTION   0 
      71 RETURN_VALUE   
>>> def scpychar(buf, n): 
...  s = '' 
...  for i in xrange(0, n): 
...   s += buf[i] 
...  return s 
... 
>>> dis.dis(scpychar) 
    2   0 LOAD_CONST    1 ('') 
       3 STORE_FAST    2 (s) 

    3   6 SETUP_LOOP    37 (to 46) 
       9 LOAD_GLOBAL    0 (xrange) 
      12 LOAD_CONST    2 (0) 
      15 LOAD_FAST    1 (n) 
      18 CALL_FUNCTION   2 
      21 GET_ITER    
     >> 22 FOR_ITER    20 (to 45) 
      25 STORE_FAST    3 (i) 

    4   28 LOAD_FAST    2 (s) 
      31 LOAD_FAST    0 (buf) 
      34 LOAD_FAST    3 (i) 
      37 BINARY_SUBSCR  
      38 INPLACE_ADD   
      39 STORE_FAST    2 (s) 
      42 JUMP_ABSOLUTE   22 
     >> 45 POP_BLOCK   

    5  >> 46 LOAD_FAST    2 (s) 
      49 RETURN_VALUE   
>>> 

Hãy so sánh nó:

  51 LOAD_FAST    2 (a) 
     54 LOAD_FAST    3 (i) 
     57 STORE_SUBSCR 

cộng

>> 62 LOAD_FAST    2 (a) 
     65 LOAD_ATTR    2 (tostring) 
     68 CALL_FUNCTION   0 

vs

  38 INPLACE_ADD   
     39 STORE_FAST 

tải chậm. CALL_FUNCTION chậm.

Tôi đã xem trong câu hỏi, cách đây một tháng, rằng ''.join(b) là cách nhanh nhất để nối mảng ký tự thành một chuỗi.

+2

Tôi đã không downvote, nhưng có thể thấy lý do tại sao nó xảy ra. Số lượng các chỉ dẫn là một ưu tiên chỉ tương quan yếu với hiệu suất. – seanmcl

+0

Nó thực sự không phải là số lượng các chỉ dẫn mà là mức tiêu thụ bộ nhớ và thời gian CPU. – enginefree

+0

@SeanMcLaughlin nhưng tôi thấy số lượng __heavy__ hướng dẫn trong scpyarr là nhiều hơn nữa. – eri

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