Próbowałem usunąć niechciane znaki z danego ciągu używając text.translate()
w Pythonie 3.4.
Minimalny kod to:
import sys
s = 'abcde12345@#@$#%$'
mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in '@#$')
print(s.translate(mapper))
Działa zgodnie z oczekiwaniami. Jednak ten sam program wykonywany w Pythonie 3.4 i Pythonie 3.5 daje dużą różnicę.
Kod do obliczania czasów to
python3 -m timeit -s "import sys;s = 'abcde12345@#@$#%$'*1000 ; mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in '@#$'); " "s.translate(mapper)"
Program w Pythonie 3.4 zajmuje 1,3 ms, podczas gdy ten sam program w Pythonie 3.5 zajmuje tylko 26,4 μs .
Co poprawiło się w Pythonie 3.5, co sprawia, że jest szybsze w porównaniu z Pythonem 3.4?
python
string
python-3.x
python-internals
python-3.5
Bhargav Rao
źródło
źródło
dict.fromkeys(ord(c) for c in '@#$')
?Odpowiedzi:
TL; DR - WYDANIE 21118
Długa historia
Josh Rosenberg odkrył, że
str.translate()
funkcja jest bardzo powolna w porównaniu dobytes.translate
, podniósł problem , stwierdzając, że:Dlaczego był
str.translate()
wolny?Głównym powodem, dla którego
str.translate()
było to bardzo powolne, był fakt, że wyszukiwanie odbywało się w słowniku Pythona.Użycie
maketrans
go pogorszyło ten problem. Podobne podejście przy użyciubytes
buduje tablicę C składającą się z 256 elementów w celu szybkiego wyszukiwania tabeli. Stąd użycie wyższego poziomu Pythonadict
powoduje, żestr.translate()
w Pythonie 3.4 jest bardzo powolny.Co się teraz stało?
Pierwszym podejściem było dodanie małej łatki translate_writer , jednak wzrost szybkości nie był tak przyjemny. Wkrótce testowano kolejną łatkę fast_translate, która dała bardzo dobre wyniki, nawet o 55% przyspieszenia.
Główna zmiana, jak widać z pliku, polega na tym, że przeszukiwanie słownika Pythona zostało zmienione na wyszukiwanie na poziomie C.
Prędkości są teraz prawie takie same jak
bytes
Mała uwaga w tym miejscu jest taka, że zwiększenie wydajności jest widoczne tylko w łańcuchach ASCII.
Jak JFSebastian wspomina w komentarzu poniżej, przed 3.5, tłumaczenie działało w ten sam sposób zarówno dla przypadków ASCII, jak i innych niż ASCII. Jednak od 3.5 ASCII sprawa jest znacznie szybsza.
Wcześniej ASCII i inne niż ASCII były prawie takie same, ale teraz widzimy wielką zmianę w wydajności.
Może to być poprawa z 71,6 μs do 2,33 μs, jak widać w tej odpowiedzi .
Poniższy kod demonstruje to
Tabela wyników:
źródło
55
%: jak pokazuje twoja odpowiedź, przyspieszenie może wynosić1000
s% .python3.5 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
(ascii) vs.python3.5 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
(non-ascii). Ta ostatnia jest dużo (10x) wolniejsza..translate()
tj. Wielkość liter ascii jest znacznie szybsza tylko w Pythonie 3.5 (nie potrzebujeszbytes.translate()
tam wydajności).