Quelques mesures de performance, en utilisant timeit
au lieu d'essayer de le faire manuellement avec time
.
Tout d'abord, Apple 2.7.2 64-bit :
In [37]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 boucles, meilleure de 3: 1.05 s par boucle
Maintenant, python.org 3.3.0 64-bit :
In [83]: %timeit collections.deque((x for x in range(10000000) if x%4 == 0), maxlen=0)
1 boucles, meilleure de 3: 1.32 s par boucle
In [84]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 boucles, meilleure de 3: 1.31 s par boucle
In [85]: %timeit collections.deque((x for x in iter(range(10000000)) if x%4 == 0), maxlen=0)
1 boucles, meilleure de 3: 1.33 s par boucle
Apparemment, 3.x range
est vraiment un peu plus lent que 2.x xrange
. Et la fonction xrange
de l'OP n'a rien à voir avec cela. (Pas étonnant, car un appel unique à l'emplacement __iter__
n'est pas susceptible d'être visible parmi les 10000000 appels à ce qui se passe dans la boucle, mais quelqu'un l'a mentionné comme une possibilité.)
Mais c'est seulement 30% plus lent. Comment l'OP a-t-il obtenu 2x plus lent ? Eh bien, si je répète les mêmes tests avec Python 32 bits, j'obtiens 1,58 vs 3,12. Donc, je suppose que c'est encore un de ces cas où 3.x a été optimisé pour les performances 64 bits de manière à nuire aux 32 bits.
Mais est-ce vraiment important ? Regardez ça, avec 3.3.0 64-bit encore une fois :
In [86]: %timeit [x for x in range(10000000) if x%4 == 0]
1 boucles, meilleure de 3: 3.65 s par boucle
Donc, construire la list
prend plus de deux fois plus de temps que l'itération entière.
Et quant à "consomme beaucoup plus de ressources que Python 2.6+", d'après mes tests, il semble qu'un 3.x range
soit exactement de la même taille qu'un 2.x xrange
— et, même s'il était 10 fois plus grand, construire la liste inutile est toujours environ 10000000 fois plus un problème que tout ce que l'itération de la plage pourrait éventuellement faire.
Et qu'en est-il d'une boucle for
explicite au lieu de la boucle C à l'intérieur de deque
?
In [87]: def consume(x):
....: for i in x:
....: pass
In [88]: %timeit consume(x for x in range(10000000) if x%4 == 0)
1 boucles, meilleure de 3: 1.85 s par boucle
Donc, presque autant de temps est perdu dans l'instruction for
que dans le travail réel d'itération de la range
.
Si vous vous inquiétez d'optimiser l'itération d'un objet plage, vous regardez probablement au mauvais endroit.
Pendant ce temps, vous continuez à demander pourquoi xrange
a été supprimé, peu importe combien de fois les gens vous disent la même chose, mais je vais le répéter : Il n'a pas été supprimé : il a été renommé en range
, et c'est le 2.x range
qui a été supprimé.
Voici quelques preuves que l'objet range
3.3 est un descendant direct de l'objet xrange
2.x (et non de la fonction range
2.x) : la source de 3.3 range
et 2.7 xrange
. Vous pouvez même voir l' historique des changements (lié, je crois, au changement qui a remplacé la dernière instance de la chaîne "xrange" n'importe où dans le fichier).
Alors, pourquoi est-ce plus lent ?
Eh bien, d'une part, ils ont ajouté beaucoup de nouvelles fonctionnalités. D'autre part, ils ont apporté toutes sortes de changements un peu partout (surtout à l'intérieur de l'itération) qui ont des effets secondaires mineurs. Et il y a eu beaucoup de travail pour optimiser de manière spectaculaire divers cas importants, même si cela rend parfois légèrement pessimiste les cas moins importants. En ajoutant tout cela, je ne suis pas surpris que l'itération d'une range
soit maintenant un peu plus lente. C'est l'un de ces cas moins importants que personne ne prendrait jamais assez de peine pour se concentrer dessus. Personne ne sera probablement jamais confronté à un cas d'utilisation réel où cette différence de performance est le point chaud de leur code.