J'ai essayé de comprendre pourquoi Python 3 prend en fait beaucoup de temps par rapport à Python 2 dans certaines situations, voici quelques cas que j'ai vérifiés à partir de python 3.4 à python 2.7.
Remarque: j'ai parcouru certaines des questions comme Pourquoi n'y a-t-il pas de fonction xrange en Python3? et boucle en python3 beaucoup plus lente que python2 et Même code plus lent dans Python3 par rapport à Python2 , mais je pense que je n'ai pas eu la véritable raison derrière ce problème.
J'ai essayé ce morceau de code pour montrer comment cela fait 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
Lorsque j'ai essayé d'exécuter ce programme avec py3.4 et py2.7, j'ai les résultats ci-dessous.
Remarque: Ces statistiques proviennent d'une machine 64 bit
Avec un processeur 2.6Ghz
Et ont calculé le temps en utilisant time.time()
en boucle unique.
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 que range
a commencé à agir comme à xrange
dans py3.4 mais comme le dit la documentation
range()
se comporte désormais commexrange()
se comportait autrefois, sauf qu'il fonctionne avec des valeurs de taille arbitraire. Ce dernier n'existe plus.
cela signifie que le changement de xrange
en range
est très similaire à un changement de nom mais fonctionne avec des valeurs arbitraires.
J'ai également vérifié le code d'octet démonté.
Ci-dessous se trouve le code d'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 est le code d'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 d'octets, ils ont produit le même code d'octets démonté.
Maintenant, je me demande quel changement de 2.7 à 3.4 est vraiment à l'origine de cet énorme changement dans le temps d'exécution dans le morceau de code donné.
La différence réside dans l'implémentation du type int
. Python 3.x utilise exclusivement le type entier de taille arbitraire (long
dans 2.x), tandis que dans Python 2.x pour valeurs jusqu'à sys.maxint
un type int
plus simple est utilisé qui utilise un simple C long
sous le capot.
Une fois que vous avez limité 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
Car sys.maxint
A été supprimé de Python 3, mais la valeur entière est fondamentalement la même.
La différence de vitesse dans Python 2 est donc limitée aux premiers (2 ** 63) - 1 entier sur 64 bits, (2 ** 31) - 1 entier sur les systèmes 32 bits.
Comme vous ne pouvez pas utiliser le type long
avec xrange()
on Python 2, je n'ai pas inclus de comparaison pour cette fonction.