Python Language
Optymalizacja wydajności
Szukaj…
Uwagi
Próbując poprawić wydajność skryptu Python, przede wszystkim powinieneś być w stanie znaleźć wąskie gardło skryptu i zauważyć, że żadna optymalizacja nie może zrekompensować złego wyboru struktur danych lub błędów w projekcie algorytmu. Identyfikowanie wąskich gardeł wydajności można wykonać, profilując skrypt. Po drugie, nie próbuj zbyt wcześnie optymalizować procesu kodowania kosztem czytelności / projektu / jakości. Donald Knuth wypowiedział się na temat optymalizacji:
„Powinniśmy zapomnieć o małej wydajności, powiedzmy około 97% czasu: przedwczesna optymalizacja jest źródłem wszelkiego zła. Nie powinniśmy jednak tracić naszych szans w tak krytycznych 3%. ”
Profilowanie kodu
Przede wszystkim powinieneś być w stanie znaleźć wąskie gardło skryptu i zauważyć, że żadna optymalizacja nie zrekompensuje złego wyboru w strukturze danych lub błędów w projekcie algorytmu. Po drugie, nie próbuj zbyt wcześnie optymalizować procesu kodowania kosztem czytelności / projektu / jakości. Donald Knuth wypowiedział się na temat optymalizacji:
„Powinniśmy zapomnieć o niewielkiej wydajności, powiedzmy w około 97% przypadków: przedwczesna optymalizacja jest źródłem wszelkiego zła. Jednak nie powinniśmy tracić naszych możliwości w tak krytycznych 3%”
Do profilowania kodu masz kilka narzędzi: cProfile
(lub wolniejszy profile
) ze standardowej biblioteki, line_profiler
i timeit
. Każdy z nich służy innym celom.
cProfile
to profil deterministyczny: wywołanie funkcji, powrót funkcji i zdarzenia wyjątku są monitorowane, a przedziały między tymi zdarzeniami są precyzyjnie regulowane (do 0,001 s). Dokumentacja biblioteki ([ https://docs.python.org/2/library/profile.html][1]) zapewnia nam prosty przypadek użycia
import cProfile
def f(x):
return "42!"
cProfile.run('f(12)')
Lub jeśli wolisz owinąć części istniejącego kodu:
import cProfile, pstats, StringIO
pr = cProfile.Profile()
pr.enable()
# ... do something ...
# ... long ...
pr.disable()
sortby = 'cumulative'
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
print s.getvalue()
Spowoduje to utworzenie wyników wyglądających jak w poniższej tabeli, gdzie możesz szybko zobaczyć, gdzie Twój program spędza większość czasu i zidentyfikować funkcje do optymalizacji.
3 function calls in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <stdin>:1(f)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
Moduł line_profiler
([ https://github.com/rkern/line_profiler][1]) jest przydatny do analizy kodu po linii. Nie jest to oczywiście możliwe do opanowania w przypadku długich skryptów, ale ma na celu urywki. Więcej informacji znajduje się w dokumentacji. Najłatwiejszym sposobem na rozpoczęcie jest użycie skryptu kernprof, jak wyjaśniono na stronie pakietu, pamiętaj, że musisz ręcznie określić funkcję (funkcje) do profilowania.
$ kernprof -l script_to_profile.py
kernprof utworzy instancję LineProfiler i wstawi ją do przestrzeni nazw __builtins__
z profilem nazw. Został napisany jako dekorator, więc w skrypcie dekorujesz funkcje, które chcesz profilować za pomocą @profile
.
@profile
def slow_function(a, b, c):
...
Domyślne zachowanie kernprof polega na umieszczeniu wyników w pliku binarnym script_to_profile.py.lprof
. Możesz powiedzieć kernprof, aby natychmiast wyświetlał sformatowane wyniki na terminalu za pomocą opcji [-v / - view]. W przeciwnym razie możesz wyświetlić wyniki później w następujący sposób:
$ python -m line_profiler script_to_profile.py.lprof
Wreszcie timeit
zapewnia prosty sposób przetestowania jednego timeit
lub małego wyrażenia zarówno z wiersza poleceń, jak i powłoki pytona. Ten moduł odpowie na pytanie, na przykład, czy szybciej jest zrozumieć listę lub użyć wbudowanej list()
podczas przekształcania zestawu w listę. Spójrz na setup
słowa kluczowego lub -s
opcji, aby dodać kod instalacyjny.
>>> import timeit
>>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
0.8187260627746582
z terminala
$ python -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 40.3 usec per loop