Niektóre pomiary wydajności, używając timeit
zamiast próbować to zrobić ręcznie time
.
Po pierwsze, 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
Teraz 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
Najwyraźniej 3.x range
naprawdę jest nieco wolniejszy niż 2.x xrange
. A funkcja PO nie xrange
ma z tym nic wspólnego. (Nic dziwnego, ponieważ jednorazowe połączenie do __iter__
automatu prawdopodobnie nie będzie widoczne wśród 10000000 połączeń z tym, co dzieje się w pętli, ale ktoś przywołał to jako możliwość).
Ale to tylko 30% wolniej. W jaki sposób PO stał się 2x tak wolny? Cóż, jeśli powtórzę te same testy z 32-bitowym Pythonem, otrzymam 1,58 vs. 3,12. Domyślam się, że jest to kolejny przypadek, w którym 3.x został zoptymalizowany pod kątem wydajności 64-bitowej w sposób, który szkodzi 32-bitowej.
Ale czy to naprawdę ma znaczenie? Sprawdź to ponownie w wersji 64.0 64-bitowej:
In [86]: %timeit [x for x in range(10000000) if x%4 == 0]
1 loops, best of 3: 3.65 s per loop
Tak więc budowanie list
zajmuje więcej niż dwa razy więcej niż cała iteracja.
A jeśli chodzi o „zużywa znacznie więcej zasobów niż Python 2.6+”, z moich testów wygląda na to, że 3.x range
ma dokładnie taki sam rozmiar jak 2.x xrange
- i nawet jeśli byłby 10 razy większy , budowanie niepotrzebnej listy wciąż stanowi około 10000000 razy większy problem niż cokolwiek, co mogłaby zrobić iteracja zakresu.
A co z wyraźną for
pętlą zamiast pętli C w środku 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
Tak więc prawie tyle samo czasu zmarnowano na for
oświadczenie, jak na faktyczną pracę nad iteracją range
.
Jeśli martwisz się optymalizacją iteracji obiektu zakresu, prawdopodobnie patrzysz w niewłaściwe miejsce.
W międzyczasie wciąż pytasz, dlaczego xrange
został usunięty, bez względu na to, ile razy ludzie mówią ci to samo, ale powtórzę to jeszcze raz: nie został usunięty: został przemianowany na range
, a 2.x range
jest tym, co zostało usunięte.
Oto kilka dowodów na to, że range
obiekt 3.3 jest bezpośrednim potomkiem xrange
obiektu 2.x (a nie range
funkcji 2.x ): źródło do 3.3range
i 2.7xrange
. Możesz nawet zobaczyć historię zmian (powiązaną, jak sądzę, ze zmianą, która zastąpiła ostatnie wystąpienie ciągu „xrange” w dowolnym miejscu pliku).
Dlaczego więc jest wolniejszy?
Po pierwsze, dodali wiele nowych funkcji. Po drugie, wprowadzili wszelkiego rodzaju zmiany w całym miejscu (szczególnie w iteracji), które mają niewielkie skutki uboczne. Dużo pracy włożono w radykalną optymalizację różnych ważnych przypadków, nawet jeśli czasami nieznacznie pesymalizuje mniej ważne przypadki. Dodaj to wszystko i nie dziwię się, że iterowanie range
tak szybko, jak to możliwe, jest teraz nieco wolniejsze. Jest to jeden z tych mniej ważnych przypadków, na których nikt nigdy by się nie przejmował, na czym mógł się skupić. Nikt nigdy nie będzie miał rzeczywistego przypadku użycia, w którym ta różnica wydajności jest hotspotem w kodzie.
range
w Python 3.x pochodzixrange
z Python 2.x. W rzeczywistościrange
usunięto Python 2.x.