2015-07-21 28 views
31

Tôi đã cố gắng hiểu tại sao Python 3 thực sự tốn nhiều thời gian so với Python 2 trong một số tình huống nhất định, dưới đây là vài trường hợp tôi đã xác minh từ python 3.4 đến python 2.7.Tại sao Python 3 chậm hơn đáng kể so với Python 2?

Lưu ý: Tôi đã trải qua một số câu hỏi như Why is there no xrange function in Python3?loop in python3 much slower than python2Same code slower in Python3 as compared to Python2, nhưng tôi cảm thấy rằng tôi không hiểu lý do thực sự đằng sau vấn đề này.

Tôi đã thử đoạn mã này để chứng tỏ nó là làm cho sự khác biệt:

MAX_NUM = 3*10**7 

# This is to make compatible with py3.4. 
try: 
    xrange 
except: 
    xrange = range 


def foo(): 
    i = MAX_NUM 
    while i> 0: 
     i -= 1 

def foo_for(): 
    for i in xrange(MAX_NUM): 
     pass 

Khi tôi đã cố gắng chạy chương trình này với py3.4 và py2.7 Tôi đã có dưới đây kết quả .

Lưu ý: Các số liệu thống kê này đi qua máy 64 bit với bộ xử lý 2.6Ghz và tính thời gian sử dụng time.time() trong vòng lặp đơn.

Output : Python 3.4 
----------------- 
2.6392083168029785 
0.9724123477935791 

Output: Python 2.7 
------------------ 
1.5131521225 
0.475143909454 

Tôi thực sự không nghĩ rằng đã có những thay đổi áp dụng cho while hoặc xrange 2,7-3,4, tôi biết range đã được bắt đầu đóng vai trò như để xrange trong py3.4 nhưng như tài liệu nói

range() hiện hoạt động như xrange() được sử dụng để xử lý, ngoại trừ nó hoạt động với các giá trị có kích thước tùy ý. Cái sau không còn tồn tại nữa.

điều này có nghĩa là thay đổi từ xrange thành range rất giống với thay đổi tên nhưng làm việc với các giá trị tùy ý.

Tôi cũng đã xác minh mã byte đã được tháo rời.

Dưới đây là các mã byte tháo rời cho chức năng foo():

Python 3.4: 
--------------- 

13   0 LOAD_GLOBAL    0 (MAX_NUM) 
       3 STORE_FAST    0 (i) 

14   6 SETUP_LOOP    26 (to 35) 
     >> 9 LOAD_FAST    0 (i) 
      12 LOAD_CONST    1 (0) 
      15 COMPARE_OP    4 (>) 
      18 POP_JUMP_IF_FALSE  34 

15   21 LOAD_FAST    0 (i) 
      24 LOAD_CONST    2 (1) 
      27 INPLACE_SUBTRACT 
      28 STORE_FAST    0 (i) 
      31 JUMP_ABSOLUTE   9 
     >> 34 POP_BLOCK 
     >> 35 LOAD_CONST    0 (None) 
      38 RETURN_VALUE 

python 2.7 
------------- 

13   0 LOAD_GLOBAL    0 (MAX_NUM) 
       3 STORE_FAST    0 (i) 

14   6 SETUP_LOOP    26 (to 35) 
     >> 9 LOAD_FAST    0 (i) 
      12 LOAD_CONST    1 (0) 
      15 COMPARE_OP    4 (>) 
      18 POP_JUMP_IF_FALSE  34 

15   21 LOAD_FAST    0 (i) 
      24 LOAD_CONST    2 (1) 
      27 INPLACE_SUBTRACT  
      28 STORE_FAST    0 (i) 
      31 JUMP_ABSOLUTE   9 
     >> 34 POP_BLOCK   
     >> 35 LOAD_CONST    0 (None) 
      38 RETURN_VALUE   

Và dưới đây là mã byte tháo rời cho chức năng foo_for():

Python: 3.4 

19   0 SETUP_LOOP    20 (to 23) 
       3 LOAD_GLOBAL    0 (xrange) 
       6 LOAD_GLOBAL    1 (MAX_NUM) 
       9 CALL_FUNCTION   1 (1 positional, 0 keyword pair) 
      12 GET_ITER 
     >> 13 FOR_ITER     6 (to 22) 
      16 STORE_FAST    0 (i) 

20   19 JUMP_ABSOLUTE   13 
     >> 22 POP_BLOCK 
     >> 23 LOAD_CONST    0 (None) 
      26 RETURN_VALUE 


Python: 2.7 
------------- 

19   0 SETUP_LOOP    20 (to 23) 
       3 LOAD_GLOBAL    0 (xrange) 
       6 LOAD_GLOBAL    1 (MAX_NUM) 
       9 CALL_FUNCTION   1 
      12 GET_ITER    
     >> 13 FOR_ITER     6 (to 22) 
      16 STORE_FAST    0 (i) 

20   19 JUMP_ABSOLUTE   13 
     >> 22 POP_BLOCK   
     >> 23 LOAD_CONST    0 (None) 
      26 RETURN_VALUE   

Nếu chúng ta so sánh cả các mã byte họ đã sản xuất cùng một mã byte bị tháo rời.

Bây giờ tôi tự hỏi thay đổi từ 2,7 đến 3,4 thực sự gây ra sự thay đổi lớn trong thời gian thực hiện trong đoạn mã đã cho.

+1

đăng mã đầy đủ và phương pháp đo – njzk2

+3

Chạy thao tác này chỉ một lần với toàn bộ thiết lập trình thông dịch * sẽ không cho bạn biết bất kỳ điều gì. Sử dụng 'timeit.timeit()' để chạy thử nghiệm thời gian thay thế. –

Trả lời

26

Sự khác biệt là trong việc triển khai loại int. Python 3.x sử dụng kiểu số nguyên có kích thước tùy ý (long trong 2.x) độc quyền, trong khi ở Python 2.x cho các giá trị lên đến sys.maxint một kiểu đơn giản hơn int được sử dụng đơn giản là C long dưới mui xe.

Khi bạn giới hạn vòng của mình thành long số nguyên, Python 3.x là nhanh hơn:

>>> from timeit import timeit 
>>> MAX_NUM = 3*10**3 
>>> def bar(): 
...  i = MAX_NUM + sys.maxsize 
...  while i > sys.maxsize: 
...   i -= 1 
... 

Python 2:

>>> timeit(bar, number=10000) 
5.704327821731567 

Python 3:

>>> timeit(bar, number=10000) 
3.7299320790334605 

tôi đã sử dụng sys.maxsize như sys.maxint đã bị bỏ từ Python 3, nhưng giá trị số nguyên về cơ bản là giống nhau .

Sự khác biệt về tốc độ trong Python 2 do đó giới hạn ở số nguyên đầu tiên (2 ** 63) - 1 trên 64 bit, (2 ** 31) - 1 số nguyên trên hệ thống 32 bit.

Vì bạn không thể sử dụng loại long với xrange() trên Python 2, tôi không bao gồm so sánh cho hàm đó.

+7

Có lý do nào bạn không muốn tối ưu hóa cho các số nguyên bên dưới 2 ** 63 không? Chúng dường như được sử dụng thường xuyên nhất ... – thebjorn

+0

@thebjorn: việc đơn giản hóa việc sử dụng một loại 'int' là quan trọng hơn. Bên cạnh đó, nếu bạn đang làm 'vòng lặp for' trên một phạm vi rộng lớn, bạn có thể làm điều gì đó sai * anyway *. –

+4

Nhưng không phải lựa chọn này làm cho tất cả các phép tính số nguyên trên ví dụ mảng chỉ số vv chậm hơn quá? Có vẻ như các ngôn ngữ khác (Smalltalk, Lisp, Haskell, Java) đi đến một số độ dài để tối ưu hóa việc đánh/bỏ hộp số nguyên, liệu những tối ưu đó có cần thiết trong một ngôn ngữ như Python không? – thebjorn

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