Подтвердить что ты не робот

Почему Python 3 значительно медленнее Python 2?

Я пытался понять, почему Python 3 на самом деле занимает много времени по сравнению с Python 2 в некоторых ситуациях, ниже приведены несколько случаев, которые я проверил с python 3.4 на python 2.7.

Примечание. Я рассмотрел некоторые вопросы, такие как Почему нет функции xrange в циклах Python3? и в 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

Когда я попытался запустить эту программу с 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

Я действительно не думаю, что были внесены изменения в while или xrange с 2,7 до 3,4, я знаю, что range был запущен, действуя как xrange в py3.4, но, как говорится в документации

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 действительно вызывает это огромное изменение в времени выполнения в данном фрагменте кода.

4b9b3361

Ответ 1

Разница заключается в реализации типа int. Python 3.x использует тип целочисленного типа произвольного размера (long в 2.x), а в 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 ограничена первыми (2 ** 63) - 1 целыми числами в 64-битных (2 ** 31) - 1 целых числах в 32-битных системах.

Поскольку вы не можете использовать тип long с xrange() на Python 2, я не использовал сравнение для этой функции.