To pytanie uzupełniające do odpowiedzi, której udzieliłem kilka dni temu . Edycja: wydaje się, że OP tego pytania użył już kodu, który mu wysłałem, aby zadać to samo pytanie , ale nie byłem tego świadomy. Przeprosiny. Podane odpowiedzi są jednak inne!
Zasadniczo zauważyłem, że:
>>> def without_else(param=False):
... if param:
... return 1
... return 0
>>> def with_else(param=False):
... if param:
... return 1
... else:
... return 0
>>> from timeit import Timer as T
>>> T(lambda : without_else()).repeat()
[0.3011460304260254, 0.2866089344024658, 0.2871549129486084]
>>> T(lambda : with_else()).repeat()
[0.27536892890930176, 0.2693932056427002, 0.27011704444885254]
>>> T(lambda : without_else(True)).repeat()
[0.3383951187133789, 0.32756996154785156, 0.3279120922088623]
>>> T(lambda : with_else(True)).repeat()
[0.3305950164794922, 0.32186388969421387, 0.3209099769592285]
... lub innymi słowy: posiadanie else
klauzuli jest szybsze, niezależnie od tego, czy if
warunek zostanie wyzwolony, czy nie.
Zakładam, że ma to związek z innym generowanym przez nich kodem bajtowym, ale czy ktoś jest w stanie potwierdzić / wyjaśnić szczegółowo?
EDYCJA: Wydaje się, że nie wszyscy są w stanie odtworzyć moje czasy, więc pomyślałem, że przydatne może być podanie pewnych informacji w moim systemie. Używam 64-bitowego systemu Ubuntu 11.10 z zainstalowanym domyślnym pythonem. python
generuje następujące informacje o wersji:
Python 2.7.2+ (default, Oct 4 2011, 20:06:09)
[GCC 4.6.1] on linux2
Oto wyniki dezasemblacji w Pythonie 2.7:
>>> dis.dis(without_else)
2 0 LOAD_FAST 0 (param)
3 POP_JUMP_IF_FALSE 10
3 6 LOAD_CONST 1 (1)
9 RETURN_VALUE
4 >> 10 LOAD_CONST 2 (0)
13 RETURN_VALUE
>>> dis.dis(with_else)
2 0 LOAD_FAST 0 (param)
3 POP_JUMP_IF_FALSE 10
3 6 LOAD_CONST 1 (1)
9 RETURN_VALUE
5 >> 10 LOAD_CONST 2 (0)
13 RETURN_VALUE
14 LOAD_CONST 0 (None)
17 RETURN_VALUE
źródło
LOAD_CONST(None); RETURN_VALUE
- jak już wspomniano, nigdy nie został osiągnięty) na końcuwith_else
. Wątpię, czy martwy kod przyspiesza działanie funkcji. Czy ktoś może podaćdis
2,7?else
iFalse
była najwolniejsza ze wszystkich (152ns). Drugi najszybszy byłTrue
bezelse
(143ns), a dwa pozostałe były w zasadzie takie same (137ns i 138ns). Nie użyłem parametru domyślnego i zmierzyłem go%timeit
w iPython.with_else
jest zauważalnie szybszy.Odpowiedzi:
To tylko domysł, i nie wymyśliłem łatwego sposobu, aby sprawdzić, czy jest to właściwe, ale mam dla ciebie teorię.
Wypróbowałem Twój kod i otrzymałem to samo z wyników,
without_else()
jest wielokrotnie nieco wolniejszy niżwith_else()
:Biorąc pod uwagę, że kod bajtowy jest identyczny, jedyną różnicą jest nazwa funkcji. W szczególności test synchronizacji sprawdza globalną nazwę. Spróbuj zmienić nazwę,
without_else()
a różnica zniknie:Domyślam się, że
without_else
ma on konflikt mieszający z czymś innym,globals()
więc globalne wyszukiwanie nazw jest nieco wolniejsze.Edycja : słownik z 7 lub 8 klawiszami ma prawdopodobnie 32 miejsca, więc na tej podstawie
without_else
występuje kolizja skrótu z__builtins__
:Aby wyjaśnić, jak działa haszowanie:
__builtins__
skróty do -1196389688, co zmniejszyło moduł wielkości stołu (32), co oznacza, że jest on przechowywany w szczelinie nr 8 stołu.without_else
hashes do 505688136, który zredukował modulo 32 do 8, więc jest kolizja. Aby rozwiązać ten problem, Python oblicza:Począwszy od:
Powtarzaj to, aż znajdziemy wolne miejsce:
co daje 17 do wykorzystania jako następnego indeksu. Na szczęście jest to darmowe, więc pętla powtarza się tylko raz. Rozmiar tablicy skrótów to potęga 2, podobnie
2**i
jak rozmiar tablicy skrótów,i
to liczba bitów użytych na podstawie wartości skrótuj
.Każda sonda w tabeli może znaleźć jedną z tych:
Szczelina jest pusta, w takim przypadku sondowanie zatrzymuje się i wiemy, że wartości nie ma w tabeli.
Szczelina nie jest używana, ale była używana w przeszłości, w takim przypadku przechodzimy do następnej wartości obliczonej jak wyżej.
Slot jest pełny, ale pełna wartość skrótu przechowywana w tabeli nie jest taka sama jak skrót klucza, którego szukamy (tak dzieje się w przypadku
__builtins__
vswithout_else
).Boks jest pełny i ma dokładnie taką wartość skrótu, jaką chcemy, a następnie Python sprawdza, czy klucz i obiekt, którego szukamy, są tym samym obiektem (co w tym przypadku będzie, ponieważ krótkie łańcuchy, które mogą być identyfikatorami, są internalizowane, więc identyczne identyfikatory używają dokładnie tego samego ciągu).
Wreszcie, gdy boks jest pełny, skrót jest dokładnie zgodny, ale klucze nie są identycznymi obiektami, wtedy i tylko wtedy Python spróbuje porównać je dla równości. Jest to stosunkowo powolne, ale w przypadku wyszukiwania nazw tak naprawdę nie powinno się zdarzyć.
źródło