Zwykle używam polecenia powłoki time
. Moim celem jest sprawdzenie, czy dane są małe, średnie, duże lub bardzo duże, ile zajmie czasu i pamięci.
Jakieś narzędzia dla Linuksa lub po prostu Pythona, aby to zrobić?
Odpowiedzi:
Spójrz na timeit , Python Profiler i Pycallgraph . Upewnij się także, aby rzucić okiem na komentarzu poniżejnikicc
wspomnieć „ SnakeViz ”. Daje Ci kolejną wizualizację danych profilowania, które mogą być pomocne.
def test():
"""Stupid test function"""
lst = []
for i in range(100):
lst.append(i)
if __name__ == '__main__':
import timeit
print(timeit.timeit("test()", setup="from __main__ import test"))
# For Python>=3.5 one can also write:
print(timeit.timeit("test()", globals=locals()))
Zasadniczo możesz przekazać go kod Pythona jako parametr ciągu, który będzie działał w określonej ilości razy i drukuje czas wykonania. Ważne informacje z dokumentacji :
timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)
UtwórzTimer
instancję z podaną instrukcją, kodem konfiguracji i funkcją timera i uruchom jejtimeit
metodę z wykonaniami liczbowymi . Opcjonalny argument globals określa przestrzeń nazw, w której ma być wykonywany kod.
... i:
Timer.timeit(number=1000000)
Czas liczba egzekucji głównego rachunku. Spowoduje to jednorazowe wykonanie instrukcji setup, a następnie zwrócenie czasu potrzebnego do wykonania instrukcji main kilka razy, mierzonego w sekundach jako liczba zmiennoprzecinkowa. Argumentem jest liczba przejść przez pętlę, domyślnie jeden milion. Do konstruktora przekazywane są instrukcja main, instrukcja setup i funkcja timera, która ma być użyta.Uwaga: domyślnie
timeit
wyłącza się tymczasowogarbage collection
podczas odliczania czasu. Zaletą tego podejścia jest to, że sprawia, że niezależne czasy są bardziej porównywalne. Ta wada polega na tym, że GC może być ważnym składnikiem wydajności mierzonej funkcji. Jeśli tak, GC można ponownie włączyć jako pierwszą instrukcję w ciągu konfiguracyjnym . Na przykład:
timeit.Timer('for i in xrange(10): oct(i)', 'gc.enable()').timeit()
Profilowanie da ci dużo bardziej szczegółowy obraz tego, co się dzieje. Oto „natychmiastowy przykład” z oficjalnych dokumentów :
import cProfile
import re
cProfile.run('re.compile("foo|bar")')
Co da ci:
197 function calls (192 primitive calls) in 0.002 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.001 0.001 <string>:1(<module>)
1 0.000 0.000 0.001 0.001 re.py:212(compile)
1 0.000 0.000 0.001 0.001 re.py:268(_compile)
1 0.000 0.000 0.000 0.000 sre_compile.py:172(_compile_charset)
1 0.000 0.000 0.000 0.000 sre_compile.py:201(_optimize_charset)
4 0.000 0.000 0.000 0.000 sre_compile.py:25(_identityfunction)
3/1 0.000 0.000 0.000 0.000 sre_compile.py:33(_compile)
Oba te moduły powinny dać ci wyobrażenie o tym, gdzie szukać wąskich gardeł.
Aby profile
zapoznać się z wynikami działania programu , zajrzyj do tego postu
UWAGA pycallgraph został oficjalnie porzucony od lutego 2018 roku . Jednak od grudnia 2020 r. Nadal działał na Pythonie 3.6. Dopóki nie ma żadnych podstawowych zmian w sposobie, w jaki Python ujawnia API do profilowania, powinien pozostać pomocnym narzędziem.
Ten moduł używa graphviz do tworzenia callgraphów, takich jak:
Możesz łatwo sprawdzić, które ścieżki zajęły najwięcej czasu według koloru. Możesz je utworzyć za pomocą pycallgraph API lub za pomocą skryptu w pakiecie:
pycallgraph graphviz -- ./mypythonscript.py
Jednak koszty ogólne są dość znaczne. Tak więc w przypadku już długotrwałych procesów tworzenie wykresu może zająć trochę czasu.
Używam prostego dekoratora do synchronizacji funkcji
def st_time(func):
"""
st decorator to calculate the total time of a func
"""
def st_func(*args, **keyArgs):
t1 = time.time()
r = func(*args, **keyArgs)
t2 = time.time()
print "Function=%s, Time=%s" % (func.__name__, t2 - t1)
return r
return st_func
timeit
Moduł była powolna i dziwne, więc napisałem tak:
def timereps(reps, func):
from time import time
start = time()
for i in range(0, reps):
func()
end = time()
return (end - start) / reps
Przykład:
import os
listdir_time = timereps(10000, lambda: os.listdir('/'))
print "python can do %d os.listdir('/') per second" % (1 / listdir_time)
Dla mnie mówi:
python can do 40925 os.listdir('/') per second
To prymitywny rodzaj testów porównawczych, ale wystarczająco dobry.
Zwykle szybko time ./script.py
sprawdzam, jak długo to potrwa. To jednak nie pokazuje pamięci, przynajmniej nie domyślnie. Możesz użyć, /usr/bin/time -v ./script.py
aby uzyskać wiele informacji, w tym użycie pamięci.
/usr/bin/time
z -v
opcją nie jest domyślnie dostępne w wielu dystrybucjach, musi zostać zainstalowane. sudo apt-get install time
w debianie, ubuntu itp. pacman -S time
archlinux
Memory Profiler dla wszystkich Twoich potrzeb związanych z pamięcią.
https://pypi.python.org/pypi/memory_profiler
Uruchom instalację pip:
pip install memory_profiler
Importuj bibliotekę:
import memory_profiler
Dodaj dekoratora do przedmiotu, który chcesz profilować:
@profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
del b
return a
if __name__ == '__main__':
my_func()
Wykonaj kod:
python -m memory_profiler example.py
Odzyskaj dane wyjściowe:
Line # Mem usage Increment Line Contents
==============================================
3 @profile
4 5.97 MB 0.00 MB def my_func():
5 13.61 MB 7.64 MB a = [1] * (10 ** 6)
6 166.20 MB 152.59 MB b = [2] * (2 * 10 ** 7)
7 13.61 MB -152.59 MB del b
8 13.61 MB 0.00 MB return a
Przykłady pochodzą z dokumentów, do których linki znajdują się powyżej.
Wystarczy popatrzeć na nosie i na jednym ze swoich wtyczek, ten jeden w szczególności.
Po zainstalowaniu nos jest skryptem na twojej ścieżce i możesz go wywołać w katalogu zawierającym skrypty Pythona:
$: nosetests
Spowoduje to przeszukanie wszystkich plików Pythona w bieżącym katalogu i wykonanie dowolnej funkcji, którą rozpozna jako test: na przykład, jako test rozpoznaje dowolną funkcję ze słowem test_ w nazwie.
Możesz więc po prostu utworzyć skrypt Pythona o nazwie test_yourfunction.py i napisać w nim coś takiego:
$: cat > test_yourfunction.py
def test_smallinput():
yourfunction(smallinput)
def test_mediuminput():
yourfunction(mediuminput)
def test_largeinput():
yourfunction(largeinput)
Wtedy musisz biec
$: nosetest --with-profile --profile-stats-file yourstatsprofile.prof testyourfunction.py
i aby odczytać plik profilu, użyj tej linii Pythona:
python -c "import hotshot.stats ; stats = hotshot.stats.load('yourstatsprofile.prof') ; stats.sort_stats('time', 'calls') ; stats.print_stats(200)"
nose
polega na hotshot. Nie jest już obsługiwany od Pythona 2.5 i jest przechowywany tylko „do użytku specjalistycznego”
Uważaj, timeit
jest bardzo powolny, po prostu zainicjowanie (lub może uruchomienie funkcji) zajmuje mojemu średniemu procesorowi 12 sekund. możesz przetestować tę zaakceptowaną odpowiedź
def test():
lst = []
for i in range(100):
lst.append(i)
if __name__ == '__main__':
import timeit
print(timeit.timeit("test()", setup="from __main__ import test")) # 12 second
time
zamiast tego użyję prostej rzeczy , na moim komputerze zwróci wynik0.0
import time
def test():
lst = []
for i in range(100):
lst.append(i)
t1 = time.time()
test()
result = time.time() - t1
print(result) # 0.000000xxxx
timeit
uruchamia twoją funkcję wiele razy, aby uśrednić szum. Liczba powtórzeń jest opcją, zobacz Benchmarking runtime w Pythonie lub późniejszą część zaakceptowanej odpowiedzi na to pytanie.
snakeviz
interaktywna przeglądarka dla cProfile
https://github.com/jiffyclub/snakeviz/
cProfile został wymieniony na https://stackoverflow.com/a/1593034/895245, a snakeviz został wymieniony w komentarzu , ale chciałem to jeszcze bardziej podkreślić.
Bardzo trudno jest debugować wydajność programu po prostu patrząc na cprofile
/ pstats
wyjście, ponieważ mogą one tylko sumaryczne czasy na funkcję po wyjęciu z pudełka.
Jednak generalnie potrzebujemy zobaczyć zagnieżdżony widok zawierający ślady stosu każdego wywołania, aby faktycznie łatwo znaleźć główne wąskie gardła.
I to jest dokładnie to, co zapewnia snakeviz w swoim domyślnym widoku „sopel lodu”.
Najpierw musisz zrzucić dane cProfile do pliku binarnego, a następnie możesz na tym snakevizować
pip install -u snakeviz
python -m cProfile -o results.prof myscript.py
snakeviz results.prof
Spowoduje to wydrukowanie adresu URL do standardowego wyjścia, które można otworzyć w przeglądarce, zawierającego żądane dane wyjściowe, które wyglądają następująco:
i możesz wtedy:
Pytanie bardziej zorientowane na profil: Jak można profilować skrypt w Pythonie?
Jeśli nie chcesz pisać standardowego kodu na czas i łatwo analizować wyniki, zapoznaj się z benchmarkit . Zapisuje również historię poprzednich przebiegów, więc łatwo jest porównać tę samą funkcję w trakcie rozwoju.
# pip install benchmarkit
from benchmarkit import benchmark, benchmark_run
N = 10000
seq_list = list(range(N))
seq_set = set(range(N))
SAVE_PATH = '/tmp/benchmark_time.jsonl'
@benchmark(num_iters=100, save_params=True)
def search_in_list(num_items=N):
return num_items - 1 in seq_list
@benchmark(num_iters=100, save_params=True)
def search_in_set(num_items=N):
return num_items - 1 in seq_set
benchmark_results = benchmark_run(
[search_in_list, search_in_set],
SAVE_PATH,
comment='initial benchmark search',
)
Wyświetla na terminalu i zwraca listę słowników z danymi z ostatniego uruchomienia. Dostępne są również punkty wejścia wiersza poleceń.
Jeśli zmienisz N=1000000
i uruchomisz ponownie
python -m cProfile -o results.prof myscript.py
. Plik oputput można wówczas bardzo ładnie przedstawione w przeglądarce za pomocą programu o nazwie SnakeViz użyciusnakeviz results.prof