2015-07-21 151 views
31

我一直在试图理解为什么Python 3在某些情况下实际上比Python 2花费了很多时间,下面是我已经从python 3.4验证到python 2.7的一些情况。为什么Python 3比Python 2慢得多?

注意:我已经通过了Why is there no xrange function in Python3?loop in python3 much slower than python2Same code slower in Python3 as compared to Python2等一些问题,但我觉得我没有得到这个问题背后的实际原因。

我已经试过这一段代码来显示它是如何做的区别:

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 

当我试图运行与py3.4和py2.7这个节目,我已经得到了以下 结果。

注意:这些统计信息是通过64 bit机器和2.6Ghz处理器计算出来的,并使用time.time()单循环计算出时间。

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

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

我真的不认为出现了2.7应用于whilexrange到3.4的变化,我知道range已经开始充当在py3.4 xrange但文件说,

range()现在的行为与xrange()相似,只是它可以处理任意大小的值。后者不再存在。

这意味着从xrange更改为range非常类似于名称更改,但使用任意值。

我已验证反汇编的字节码。

下面是功能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   

而且下面是功能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   

如果我们比较这两个字节的代码,他们已经生产相同的反汇编字节码。

现在我想知道从2.7到3.4的变化是否真的会导致给定代码片段执行时间的巨大变化。

+1

发布全部代码和测量方法 – njzk2

+3

只用一次*整个解释器设置*就不会告诉你任何东西。使用'timeit.timeit()'来代替运行时间测试。 –

回答

26

不同之处在于执行int类型。 Python 3.x专门使用任意大小的整数类型(2.x中的long),而在Python 2.x中,值最大为sys.maxint时,使用简单的int类型,它使用简单的C long

一旦你限制你的循环为long整数,Python 3。x是更快:

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

我用sys.maxsize作为sys.maxint从Python 3的下降,但整数值基本相同。

Python 2中的速度差异因此限制在32位系统上的64位(2 ** 31) - 1整数上的第一个(2 ** 63) - 1整数。

由于您不能在Python 2上使用long类型和xrange(),所以我没有包含该函数的比较。

+7

是否有任何理由不希望为2 ** 63以下的整数进行优化?他们似乎是最常用的... – thebjorn

+0

@thebjorn:简化使用一个'int'类型更重要。另外,如果你在这个大范围内执行'for'循环,你可能会做一些错误的事情*无论如何*。 –

+4

但是,这种选择是否使得例如数组索引等也比较慢?似乎其他语言(Smalltalk,Lisp,Haskell,Java)为了优化整数的装箱/拆箱而花费了一些时间,这些优化在像Python这样的语言中是多余的? – thebjorn

相关问题