Vor kurzem habe ich mit Python3 angefangen und es fehlt an Xrange-Verletzungen.
Einfaches Beispiel:
1) Python2:
from time import time as t
def count():
st = t()
[x for x in xrange(10000000) if x%4 == 0]
et = t()
print et-st
count()
2) Python3:
from time import time as t
def xrange(x):
return iter(range(x))
def count():
st = t()
[x for x in xrange(10000000) if x%4 == 0]
et = t()
print (et-st)
count()
Die Ergebnisse sind jeweils:
1) 1.53888392448 2) 3,215819835662842
Warum das? Ich meine, warum wurde Xrange entfernt? Es ist ein großartiges Werkzeug, um es zu lernen. Für die Anfänger genauso wie ich, als wären wir alle irgendwann gewesen. Warum es entfernen? Kann mich jemand auf die richtige PEP verweisen, ich kann sie nicht finden.
Prost.
Einige Leistungsmessungen verwenden timeit
, anstatt zu versuchen, dies manuell mit time
zu tun.
Erstens, Apple 2.7.2 64-Bit:
In [37]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.05 s per loop
Nun, 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 loops, best of 3: 1.32 s per loop
In [84]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.31 s per loop
In [85]: %timeit collections.deque((x for x in iter(range(10000000)) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.33 s per loop
Offensichtlich ist 3.x range
tatsächlich etwas langsamer als 2.x xrange
. Und die Funktion xrange
des OP hat nichts damit zu tun. (Kein Wunder, da ein einmaliger Aufruf des __iter__
-Slots bei 10000000-Aufrufen an das, was in der Schleife passiert, wahrscheinlich nicht sichtbar ist, aber jemand hat es als Möglichkeit angesprochen.)
Aber es ist nur 30% langsamer. Wie wurde das OP 2x so langsam? Wenn ich dieselben Tests mit 32-Bit-Python wiederhole, bekomme ich 1,58 vs. 3,12. Meine Vermutung ist also, dass dies ein weiterer Fall ist, in dem 3.x für 64-Bit-Leistung in einer Weise optimiert wurde, die 32-Bit verletzt.
Aber ist es wirklich wichtig? Überprüfen Sie dies mit 3.3.0 64-Bit noch einmal:
In [86]: %timeit [x for x in range(10000000) if x%4 == 0]
1 loops, best of 3: 3.65 s per loop
Das Erstellen von list
dauert also mehr als doppelt so lange wie die gesamte Iteration.
Und was "bei weitem mehr Ressourcen als bei Python 2.6+" anbelangt, sieht es bei meinen Tests so aus, als sei ein 3.x range
genau die gleiche Größe wie ein 2.x xrange
und sogar wenn Es war 10 mal so groß, das Erstellen der unnötigen Liste ist immer noch etwa 10000000 mal mehr ein Problem als alles, was die Range-Iteration tun könnte.
Und was ist mit einer expliziten for
Schleife anstelle der C-Schleife in 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 loops, best of 3: 1.85 s per loop
In der for
-Anweisung wurde also fast genauso viel Zeit verschwendet wie in der eigentlichen Arbeit des Iterierens der range
.
Wenn Sie befürchten, die Iteration eines Bereichsobjekts zu optimieren, suchen Sie wahrscheinlich am falschen Ort.
In der Zwischenzeit fragen Sie immer wieder, warum xrange
entfernt wurde, egal wie oft Sie dasselbe sagen, aber ich wiederhole es noch einmal: Es wurde nicht entfernt: Es wurde in range
umbenannt Die 2.x range
wurde entfernt.
Hier ist ein Beweis dafür, dass das 3.3 range
-Objekt ein direkter Nachkomme des 2.x xrange
-Objekts ist (und nicht der 2.x range
-Funktion): die Quelle für 3.3 range
und 2.7 xrange
. Sie können sogar den change history sehen (verknüpft mit meiner Meinung nach die Änderung, die die letzte Instanz des Strings "xrange" an einer beliebigen Stelle in der Datei ersetzt hat).
Warum ist es langsamer?
Zum einen haben sie viele neue Funktionen hinzugefügt. Zum anderen haben sie überall (vor allem in der Iteration) alle möglichen Änderungen vorgenommen, die geringfügige Nebenwirkungen haben. Und es gab eine Menge Arbeit, um verschiedene wichtige Fälle dramatisch zu optimieren, auch wenn dies manchmal weniger wichtige Fälle pessimiert. Addieren Sie das alles zusammen und ich bin nicht überrascht, dass das Durchlaufen eines range
so schnell wie möglich jetzt etwas langsamer ist. Es ist einer dieser unwichtigen Fälle, an denen sich niemand jemals genug interessieren würde. Wahrscheinlich wird es niemals einen realen Anwendungsfall geben, bei dem dieser Leistungsunterschied der Hotspot in seinem Code ist.
Python3s Bereich ist Python2s Xrange. Es ist nicht nötig, einen Iter um ihn zu wickeln. Um eine aktuelle Liste in Python3 zu erhalten, müssen Sie list(range(...))
verwenden.
Wenn Sie etwas möchten, das mit Python2 und Python3 funktioniert, probieren Sie es aus
try:
xrange
except NameError:
xrange = range
Der range
-Typ von Python 3 funktioniert genauso wie xrange
von Python 2. Ich bin nicht sicher, warum Sie eine Verlangsamung sehen, da der von Ihrer xrange
-Funktion zurückgegebene Iterator genau das ist, was Sie erhalten würden, wenn Sie range
direkt iterieren.
Ich kann die Verlangsamung meines Systems nicht reproduzieren. So habe ich getestet:
Python 2 mit xrange
:
Python 2.7.3 (default, Apr 10 2012, 23:24:47) [MSC v.1500 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import timeit
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)
18.631936646865853
Python 3, mit range
, ist ein bisschen schneller:
Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import timeit
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)
17.31399508687869
Ich habe kürzlich erfahren, dass der range
-Typ von Python 3 einige weitere nützliche Funktionen hat, z. B. Unterstützung für das Slicing: range(10,100,2)[5:25:5]
ist range(15, 60, 10)
!
xrange von Python 2 ist ein Generator und implementiert einen Iterator, während range nur eine Funktion ist .. In Python3 weiß ich nicht, warum der Xrange-Bereich verlassen wurde.
Eine Möglichkeit, Ihren Python2-Code zu korrigieren, ist:
import sys
if sys.version_info >= (3, 0):
def xrange(*args, **kwargs):
return iter(range(*args, **kwargs))