为什么Python 3比Python 2慢得多?

gsb*_*eng 36 python performance cpython python-2.7 python-3.x

我一直试图理解为什么Python 3在某些情况下与Python 2相比实际上需要花费很多时间,下面是我从python 3.4到python 2.7验证的少数情况.

注意:我已经解决了一些问题,比如为什么Python3中没有xrange函数?在python3比python2慢得多环路比同一代码在Python3慢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
Run Code Online (Sandbox Code Playgroud)

当我尝试用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
Run Code Online (Sandbox Code Playgroud)

我真的不认为出现了适用于变更whilexrange从2.7到3.4,我知道range已经开始作为对xrange在py3.4但文件说,

range()现在表现得像xrange()以前的行为,除了它适用于任意大小的值.后者不再存在.

这意味着从更改xrangerange非常等于名称更改但使用任意值.

我也验证了反汇编的字节码.

下面是函数的反汇编字节代码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        
Run Code Online (Sandbox Code Playgroud)

以下是函数的反汇编字节代码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        
Run Code Online (Sandbox Code Playgroud)

如果我们比较两个字节代码,它们就会产生相同的反汇编字节代码.

现在我想知道从2.7到3.4的变化是否真正导致给定代码段中执行时间的这一巨大变化.

Mar*_*ers 34

不同之处在于int类型的实现.蟒3.X使用任意大小的整数类型(long在2.X)排他地,而在Python 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
... 
Run Code Online (Sandbox Code Playgroud)

Python 2:

>>> timeit(bar, number=10000)
5.704327821731567
Run Code Online (Sandbox Code Playgroud)

Python 3:

>>> timeit(bar, number=10000)
3.7299320790334605
Run Code Online (Sandbox Code Playgroud)

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

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

由于您无法在Python 2上使用该long类型xrange(),因此我没有包含该函数的比较.

  • 有什么理由你不想优化低于2**63的整数?它们似乎是最常用的...... (9认同)
  • 但是这个选择不会使例如数组索引等的所有整数计算都变慢吗?似乎其他语言(Smalltalk,Lisp,Haskell,Java)为了优化整数的装箱/拆箱而有所不同,这些优化在像Python这样的语言中是多余的吗? (7认同)
  • @MartijnPieters它的性能仍然不如OP的测试显示的那样. (2认同)