Pourquoi Python 3 est considérablement plus lent que Python 2? [dupliquer]

cette question a déjà une réponse ici:

j'ai essayé de comprendre pourquoi Python 3 prend en fait beaucoup de temps par rapport à Python 2 dans certaines situations, ci-dessous sont quelques cas J'ai vérifié de Python 3.4 à Python 2.7.

Note: J'ai passé en revue certaines des questions comme pourquoi n'y a-t-il pas de fonction xrange en Python3? et boucle en python3 beaucoup plus lent que python2 et même code plus lent en Python3 par rapport à Python2 , mais je pense que je n'ai pas eu la raison réelle derrière cette question.

j'ai essayé ce morceau de code pour montrer comment il est de faire la différence:

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

quand j'ai essayé de gérer ce programme avec py3.4 et py2.7 résultat.

Note: Ces statistiques proviennent d'une machine 64 bit avec 2.6Ghz processeur et calculé le temps en utilisant time.time() en boucle simple.

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

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

Je ne pense vraiment pas qu'il y ait eu des changements appliqués à while ou xrange de 2.7 à 3.4, je sais range a commencé à jouer le rôle de xrange dans py3.4 mais comme la documentation dit

range() se comporte maintenant comme xrange() utilisé pour se comporter, sauf qu'il fonctionne avec des valeurs de taille arbitraire. Ce dernier n'existe plus.

cela signifie passer de xrange à range est très proche d'un changement de nom mais travailler avec des valeurs arbitraires.

j'ai vérifié le code byte démonté ainsi.

ci-dessous est le code octet démonté pour la fonction 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        

et ci-dessous le code octet démonté pour la fonction 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        

si nous comparons les deux codes octets, ils ont produit le même code octet démonté.

maintenant je me demande ce que le changement de 2.7 à 3.4 est réellement à l'origine de ce changement énorme dans le temps d'exécution dans la pièce Donnée de code.

34
demandé sur Community 2015-07-21 23:09:02

1 réponses

la différence réside dans la mise en œuvre du type int . Python 3.x utilise le type entier de taille arbitraire ( long en 2.x) exclusivement, en Python 2.x pour les valeurs jusqu'à sys.maxint un type plus simple int est utilisé qui utilise un simple C long sous la hotte.

une fois que vous limitez vos boucles à long entiers, Python 3.x est plus rapide:

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

j'ai utilisé sys.maxsize comme sys.maxint a été supprimé de Python 3, mais la valeur entière est fondamentalement la même.

la différence de vitesse en Python 2 est donc limitée à la première (2 ** 63) - 1 nombre entier sur 64 bits, (2 ** 31)-1 nombre entier sur les systèmes 32 bits.

puisque vous ne pouvez pas utiliser le type long avec xrange() sur Python 2, Je n'ai pas inclus une comparaison de cette fonction.

33
répondu Martijn Pieters 2015-07-21 20:37:07