Jak profilować skrypt w języku Python?

1282

Project Euler i inne konkursy kodowania często mają maksymalny czas do uruchomienia lub ludzie chwalą się szybkością działania danego rozwiązania. W Pythonie czasami podejścia są nieco kludgey - tj. Dodawanie kodu czasowego do __main__.

W jaki sposób można profilować, ile czasu zajmuje uruchomienie programu w języku Python?

Chris Lawlor
źródło
113
Programy euler projektu nie powinny wymagać profilowania. Albo masz algorytm, który działa w mniej niż minutę, albo masz całkowicie zły algorytm. „Strojenie” rzadko jest właściwe. Zasadniczo musisz przyjąć nowe podejście.
S.Lott
105
S.Lott: Profilowanie jest często pomocnym sposobem na określenie, które podprogramy są powolne. Podprogramy, które zajmują dużo czasu, są świetnymi kandydatami do ulepszania algorytmów.
stalepretzel

Odpowiedzi:

1369

Python zawiera profiler o nazwie cProfile . Podaje nie tylko całkowity czas działania, ale także czasy każdej funkcji osobno i informuje, ile razy każda funkcja została wywołana, co ułatwia określenie, gdzie należy dokonać optymalizacji.

Możesz go wywołać z poziomu kodu lub z interpretera:

import cProfile
cProfile.run('foo()')

Jeszcze bardziej przydatne, możesz uruchomić cProfile podczas uruchamiania skryptu:

python -m cProfile myscript.py

Aby było to jeszcze łatwiejsze, stworzyłem mały plik wsadowy o nazwie „profile.bat”:

python -m cProfile %1

Więc wszystko, co muszę zrobić, to uruchomić:

profile euler048.py

I dostaję to:

1007 function calls in 0.061 CPU seconds

Ordered by: standard name
ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.000    0.000    0.061    0.061 <string>:1(<module>)
 1000    0.051    0.000    0.051    0.000 euler048.py:2(<lambda>)
    1    0.005    0.005    0.061    0.061 euler048.py:2(<module>)
    1    0.000    0.000    0.061    0.061 {execfile}
    1    0.002    0.002    0.053    0.053 {map}
    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler objects}
    1    0.000    0.000    0.000    0.000 {range}
    1    0.003    0.003    0.003    0.003 {sum}

EDYCJA: Zaktualizowano link do dobrego zasobu wideo z PyCon 2013 zatytułowanego Profilowanie w języku Python
Także przez YouTube .

Chris Lawlor
źródło
251
Przydatne jest również sortowanie wyników, które można wykonać przełącznikiem -s, na przykład: „-s time”. Możesz użyć opcji sortowania skumulowanego / nazwy / czasu / pliku.
Jiri,
19
Warto również zauważyć, że możesz użyć modułu cProfile z ipython, używając funkcji magicznej% prun (uruchomienie profilu). Najpierw zaimportuj moduł, a następnie wywołaj główną funkcję za pomocą% prun: import euler048; % prun euler048.main ()
RussellStewart
53
Do wizualizacji zrzutów cProfile (utworzonych przez python -m cProfile -o <out.profile> <script>) RunSnakeRun , wywoływany jako runsnake <out.profile>jest nieoceniony.
Lily Chung
13
@NeilG nawet dla Pythona 3, cprofilenadal zaleca się profile.
trichoplax
17
Do wizualizacji zrzutów cProfile RunSnakeRun nie był aktualizowany od 2011 roku i nie obsługuje Python3. Należy użyć snakeviz zamiast
Giacomo Tecya Pigani
423

Jakiś czas temu zrobiłem, pycallgraphktóry generuje wizualizację z twojego kodu Python. Edycja: Zaktualizowałem przykład do pracy z 3.3, najnowszą wersją od tego pisania.

Po pip install pycallgraphzainstalowaniu GraphViz możesz uruchomić go z wiersza poleceń:

pycallgraph graphviz -- ./mypythonscript.py

Możesz także profilować określone części kodu:

from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput

with PyCallGraph(output=GraphvizOutput()):
    code_to_profile()

Każdy z nich wygeneruje pycallgraph.pngplik podobny do poniższego obrazu:

wprowadź opis zdjęcia tutaj

gak
źródło
43
Czy kolorujesz na podstawie liczby połączeń? Jeśli tak, powinieneś pokolorować według czasu, ponieważ funkcja z największą liczbą wywołań nie zawsze zajmuje najwięcej czasu.
czerwony
21
@red Możesz dostosować kolory, jak chcesz, a nawet niezależnie dla każdego pomiaru. Na przykład czerwony dla połączeń, niebieski dla czasu, zielony dla użycia pamięci.
gak
2
otrzymuję ten błądTraceback (most recent call last): /pycallgraph.py", line 90, in generate output.done() File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 94, in done source = self.generate() File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 143, in generate indent_join.join(self.generate_attributes()), File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 169, in generate_attributes section, self.attrs_from_dict(attrs), ValueError: zero length field name in format
Ciasto piekarz
3
Zaktualizowałem to, aby wspomnieć, że musisz zainstalować GraphViz, aby rzeczy działały zgodnie z opisem. Na Ubuntu to jest po prostu sudo apt-get install graphviz.
mlissner,
2
To wymaga trochę pracy, aby zainstalować tutaj. 3 kroki, aby pomóc. 1. Zainstaluj przez pip, 2. Zainstaluj GraphViz przez exe 3. Ustaw zmienne ścieżki do katalogu GraphViz 4. Dowiedz się, jak naprawić wszystkie pozostałe błędy. 5. Dowiedzieć się, gdzie zapisuje plik png?
bagno
199

Warto zauważyć, że korzystanie z profilera działa (domyślnie) tylko w głównym wątku i nie uzyskasz żadnych informacji z innych wątków, jeśli ich użyjesz. Może to być trochę kłopotliwe, ponieważ jest to całkowicie nie wspomniane w dokumentacji profilera .

Jeśli chcesz także profilować wątki, spójrz na threading.setprofile()funkcję w dokumentach.

Możesz również utworzyć własną threading.Threadpodklasę, aby to zrobić:

class ProfiledThread(threading.Thread):
    # Overrides threading.Thread.run()
    def run(self):
        profiler = cProfile.Profile()
        try:
            return profiler.runcall(threading.Thread.run, self)
        finally:
            profiler.dump_stats('myprofile-%d.profile' % (self.ident,))

i użyj tej ProfiledThreadklasy zamiast standardowej. Może dać ci większą elastyczność, ale nie jestem pewien, czy warto, szczególnie jeśli używasz kodu innej firmy, który nie używałby twojej klasy.

Joe Shaw
źródło
1
Nie widzę też w dokumentacji żadnego odwołania do wywołania run. Patrząc na cProfile.py, nie jestem pewien, dlaczego używasz funkcji threading.Thread.run ani siebie jako argumentu. Spodziewałbym się tutaj odniesienia do metody uruchamiania innego wątku.
PypeBros,
Nie ma go w dokumentacji, ale jest w module. Zobacz hg.python.org/cpython/file/6bf07db23445/Lib/cProfile.py#l140 . Pozwala to na profilowanie określonego wywołania funkcji, aw naszym przypadku chcemy profilować funkcję Wątka target, która jest wykonywana przez to threading.Thread.run()wywołanie. Ale jak powiedziałem w odpowiedzi, prawdopodobnie nie warto podklasować wątku, ponieważ żaden kod innej firmy nie użyje go, a zamiast tego użyje threading.setprofile().
Joe Shaw,
9
wydaje się, że również owijanie kodu profiler.enable () i profiler.disable () działa całkiem dobrze. Zasadniczo to robi runcall i nie wymusza żadnej liczby argumentów lub podobnych rzeczy.
PypeBros,
1
Joe, czy wiesz, jak profiler gra z asyncio w Pythonie 3.4?
Nick Chammas,
148

Python wiki to świetna strona do profilowania zasobów: http://wiki.python.org/moin/PythonSpeed/PerformanceTips#Profiling_Code

podobnie jak dokumentacja Pythona: http://docs.python.org/library/profile.html

jak pokazuje Chris Lawlor cProfile to świetne narzędzie, które można łatwo wykorzystać do drukowania na ekranie:

python -m cProfile -s time mine.py <args>

lub złożyć:

python -m cProfile -o output.file mine.py <args>

PS> Jeśli używasz Ubuntu, pamiętaj o zainstalowaniu profilu python

apt-get install python-profiler 

Jeśli wyprowadzasz dane do pliku, możesz uzyskać ładne wizualizacje za pomocą następujących narzędzi

PyCallGraph: narzędzie do tworzenia
instalacji grafów połączeń :

 pip install pycallgraph

biegać:

 pycallgraph mine.py args

widok:

 gimp pycallgraph.png

Możesz użyć, co chcesz, aby wyświetlić plik png, użyłem gimp
Niestety często dostaję

kropka: wykres jest za duży dla map bitowych renderera Cairo. Skalowanie o 0,257079 w celu dopasowania

co sprawia, że ​​moje obrazy są wyjątkowo małe. Generalnie tworzę pliki svg:

pycallgraph -f svg -o pycallgraph.svg mine.py <args>

PS> upewnij się, że zainstalowałeś graphviz (który zapewnia program kropkowy):

pip install graphviz

Alternatywne wykresy przy użyciu gprof2dot przez @maxy / @quodlibetor:

pip install gprof2dot
python -m cProfile -o profile.pstats mine.py
gprof2dot -f pstats profile.pstats | dot -Tsvg -o mine.svg
brent.payne
źródło
12
gprof2dot może również wykonywać te wykresy. Myślę, że wynik jest nieco ładniejszy ( przykład ).
maksymalnie
2
graphviz jest również wymagany, jeśli używasz OSX
Vaibhav Mishra
134

Komentarz @ Maxy'ego do tej odpowiedzi pomógł mi na tyle, że myślę, że zasługuje na swoją własną odpowiedź: miałem już pliki .pstats wygenerowane przez cProfile i nie chciałem ponownie uruchamiać rzeczy z pycallgraph, więc użyłem gprof2dot i zrobiłem się ładny svgs:

$ sudo apt-get install graphviz
$ git clone https://github.com/jrfonseca/gprof2dot
$ ln -s "$PWD"/gprof2dot/gprof2dot.py ~/bin
$ cd $PROJECT_DIR
$ gprof2dot.py -f pstats profile.pstats | dot -Tsvg -o callgraph.svg

i BLAM!

Używa kropki (tej samej, której używa pycallgraph), więc dane wyjściowe wyglądają podobnie. Mam wrażenie, że gprof2dot traci mniej informacji:

Przykładowy wynik gprof2dot

quodlibetor
źródło
1
Dobre podejście, działa naprawdę dobrze, ponieważ możesz przeglądać SVG w Chrome itp. I skalować je w górę / w dół. Trzecia linia ma literówkę, powinna być: ln -s pwd/gprof2dot/gprof2dot.py $ HOME / bin (lub użyj ln -s $ PWD / gprof2dot / gprof2dot.py ~ / bin w większości powłok - akcent poważny jest traktowany jako formatowanie w pierwszej kolejności wersja).
RichVel
2
Ach, dobra racja. lnNiemal za każdym razem otrzymuję błędną kolejność argumentów.
quodlibetor
7
sztuczka polega na tym, aby pamiętać, że ln i cp mają tę samą kolejność argumentów - pomyśl o tym jak o „kopiowaniu pliku 1 do pliku 2 lub katalogu 2, ale tworzeniu linku”
RichVel
To ma sens, myślę, że rzuca mnie „TARGET” na stronie.
quodlibetor
Jak udało ci się zaokrąglić rogi? Uważam, że poprawia to czytelność. Po prostu dostaję brzydkie ostre rogi, które nie są fajne w obecności wielu krawędzi wokół pudeł.
Hibou57
77

Podczas badania tego tematu natrafiłem na przydatne narzędzie o nazwie SnakeViz . SnakeViz to internetowe narzędzie do wizualizacji profilowania. Jest bardzo łatwy w instalacji i obsłudze. Zwykle używam go do generowania pliku statystyk%prun a następnie analizy w SnakeViz.

Główną zastosowaną techniką jest karta Sunburst jak pokazano poniżej, w którym hierarchia wywołań funkcji jest ułożona jako warstwy łuków i informacji o czasie zakodowanych w ich szerokościach kątowych.

Najlepszą rzeczą jest interakcja z tabelą. Na przykład, aby powiększyć, można kliknąć łuk, a łuk i jego potomkowie zostaną powiększeni jako nowy promień słońca, aby wyświetlić więcej szczegółów.

wprowadź opis zdjęcia tutaj

zaxliu
źródło
1
Odpowiedź CodeCabbie zawiera (krótką) instrukcję instalacji i pokazuje, jak (łatwo) używać SnakeViz.
Oren Milman,
Tutaj przeczytałem dobry przewodnik IMHO, jak korzystać z profilowania dla Pythona w notatniku jupyter
Alexei Martianov
72

Najprostszy i najszybszy sposób na znalezienie dokąd zmierza cały czas.

1. pip install snakeviz

2. python -m cProfile -o temp.dat <PROGRAM>.py

3. snakeviz temp.dat

Rysuje wykres kołowy w przeglądarce. Największym elementem jest funkcja problemu. Bardzo prosta.

CodeCabbie
źródło
1
To było bardzo pomocne. Dziękuję Ci.
jippyjoe4
55

Myślę, że cProfilejest to świetne do profilowania, a jednocześnie kcachegrinddo wizualizacji wyników. pyprof2calltreePomiędzy uchwytami konwersja plików.

python -m cProfile -o script.profile script.py
pyprof2calltree -i script.profile -o script.calltree
kcachegrind script.calltree

Aby zainstalować wymagane narzędzia (przynajmniej na Ubuntu):

apt-get install kcachegrind
pip install pyprof2calltree

Wynik:

Zrzut ekranu wyniku

Federico
źródło
9
Użytkownicy Mac zainstalować brew install qcachegrindi substitude każda kcachegrind ze qcachegrind w opisie udanej profilowania.
Kevin Katzke,
Musiałem to zrobić, aby to zadziałało:export QT_X11_NO_MITSHM=1
Yonatan Simson
41

Warto również wspomnieć o przeglądarce Run zrzutów graficznych cProfile GUI RunSnakeRun . Pozwala sortować i wybierać, przybliżając odpowiednie części programu. Rozmiary prostokątów na zdjęciu są proporcjonalne do czasu. Jeśli najedziesz myszką na prostokąt, podświetli to wywołanie w tabeli i wszędzie na mapie. Dwukrotne kliknięcie prostokąta powoduje powiększenie tej części. Pokaże ci, kto wywołuje tę część i co ta część wzywa.

Informacje opisowe są bardzo pomocne. Pokazuje kod tego bitu, który może być pomocny w przypadku wywołań wbudowanej biblioteki. Mówi ci, jaki plik i jaką linię znaleźć kod.

Chciałbym również wskazać, że PO powiedział „profilowanie”, ale wygląda na to, że miał na myśli „timing”. Pamiętaj, że programy będą działały wolniej podczas profilowania.

wprowadź opis zdjęcia tutaj

Pete
źródło
34

Ładnym modułem profilującym jest line_profiler (wywoływany za pomocą skryptu kernprof.py). Można go pobrać tutaj .

Rozumiem, że cProfile podaje tylko informacje o całkowitym czasie spędzonym na każdej funkcji. Poszczególne wiersze kodu nie są taktowane. Jest to problem w informatyce naukowej, ponieważ często jedna linia może zająć dużo czasu. Ponadto, jak pamiętam, cProfile nie złapał czasu, który spędziłem na powiedzmy numpy.dot.

Ian Langmore
źródło
34

Niedawno stworzyłem tuńczyka do wizualizacji środowiska wykonawczego Python i importowania profili; może to być pomocne tutaj.

wprowadź opis zdjęcia tutaj

Zainstaluj za pomocą

pip install tuna

Utwórz profil środowiska wykonawczego

python3 -m cProfile -o program.prof yourfile.py

lub profil importu (wymagany Python 3.7+)

python3 -X importprofile yourfile.py 2> import.log

Następnie po prostu uruchom tuńczyka na pliku

tuna program.prof
Nico Schlömer
źródło
33

pprofile

line_profiler(już tutaj zaprezentowane) również zainspirowane pprofile, co opisano jako:

Granularność linii, deterministyczny i statystyczny profiler oparty na czystym pythonie

Zapewnia ziarnistość linii, ponieważ line_profilerjest czystym Pythonem, może być używany jako samodzielna komenda lub moduł, a nawet może generować pliki w formacie callgrind, które można łatwo analizować [k|q]cachegrind.

vprof

Istnieje również vprof , pakiet Python opisany jako:

[...] zapewniając bogate i interaktywne wizualizacje dla różnych charakterystyk programu Python, takich jak czas działania i zużycie pamięci.

Mapa ciepła

BenC
źródło
14

Istnieje wiele świetnych odpowiedzi, ale albo używają wiersza polecenia, albo zewnętrznego programu do profilowania i / lub sortowania wyników.

Naprawdę brakowało mi sposobu, w jaki mógłbym użyć w swoim IDE (eclipse-PyDev) bez dotykania wiersza poleceń lub instalowania czegokolwiek. Więc oto jest.

Profilowanie bez wiersza poleceń

def count():
    from math import sqrt
    for x in range(10**5):
        sqrt(x)

if __name__ == '__main__':
    import cProfile, pstats
    cProfile.run("count()", "{}.profile".format(__file__))
    s = pstats.Stats("{}.profile".format(__file__))
    s.strip_dirs()
    s.sort_stats("time").print_stats(10)

Aby uzyskać więcej informacji, zobacz dokumenty lub inne odpowiedzi.

David Mašek
źródło
na przykład profil drukuje {mapa} lub {xxx}. skąd mam wiedzieć, która metoda {xxx} jest wywoływana z którego pliku? mój profil drukuje {metoda „kompresuj” obiekty „zlib.Compress”} zajmuje większość czasu, ale nie używam żadnego zlib, więc myślę, że niektóre funkcje wywołania numpy mogą go użyć. Skąd mam wiedzieć, który dokładnie plik i linia zajmuje dużo czasu?
machen
12

Po odpowiedzi Joe Shawa, że ​​kod wielowątkowy nie działa zgodnie z oczekiwaniami, doszedłem do wniosku, że runcallmetoda w cProfile po prostu działa self.enable()i self.disable()wywołuje profilowane wywołanie funkcji, więc możesz po prostu zrobić to sam i mieć dowolny kod pomiędzy nimi minimalna ingerencja w istniejący kod.

PypeBros
źródło
3
Doskonała wskazówka! Szybki rzut oka na cprofile.pykod źródłowy ujawnia, że właśnie to runcall()działa. Mówiąc dokładniej, po utworzeniu instancji profilu za pomocą prof = cprofile.Profile(), natychmiast wywołuje prof.disable(), a następnie dodaje prof.enable()i prof.disable()wywołuje sekcję kodu, który chcesz profilować.
martineau,
Jest to bardzo pomocne, ale wydaje się, że kod pomiędzy włączaniem i wyłączaniem nie jest profilowany - tylko funkcje, które wywołuje. Czy mam to prawo? Musiałbym owinąć ten kod w wywołanie funkcji, aby liczyć się do dowolnej liczby w print_stats ().
Bob Stein
10

W Virtaal za źródło jest bardzo przydatna klasa i dekorator, który może wnieść do profilowania (nawet dla konkretnych metod / funkcji) bardzo łatwe. Dane wyjściowe można następnie bardzo wygodnie przeglądać w KCacheGrind.

Walter
źródło
1
Dziękuję za ten klejnot. FYI: Może być używany jako samodzielny moduł z dowolnym kodem, podstawa kodu Virtaal nie jest wymagana. Po prostu zapisz plik w profiling.py i zaimportuj profile_func (). Użyj @profile_func () jako dekoratora dowolnej funkcji, której potrzebujesz do profilowania i altówki. :)
Amjith,
9

cProfile jest świetny do szybkiego profilowania, ale przez większość czasu kończyło się to z błędami. Funkcja runctx rozwiązuje ten problem, poprawnie inicjując środowisko i zmienne. Mam nadzieję, że może się przydać komuś:

import cProfile
cProfile.runctx('foo()', None, locals())
Datageek
źródło
7

Jeśli chcesz utworzyć skumulowany profiler, co oznacza uruchomienie funkcji kilka razy z rzędu i obejrzenie sumy wyników.

możesz użyć tego cumulative_profilerdekoratora:

jest specyficzny dla python> = 3.6, ale możesz go usunąć, nonlocalaby działał na starszych wersjach.

import cProfile, pstats

class _ProfileFunc:
    def __init__(self, func, sort_stats_by):
        self.func =  func
        self.profile_runs = []
        self.sort_stats_by = sort_stats_by

    def __call__(self, *args, **kwargs):
        pr = cProfile.Profile()
        pr.enable()  # this is the profiling section
        retval = self.func(*args, **kwargs)
        pr.disable()

        self.profile_runs.append(pr)
        ps = pstats.Stats(*self.profile_runs).sort_stats(self.sort_stats_by)
        return retval, ps

def cumulative_profiler(amount_of_times, sort_stats_by='time'):
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            nonlocal function, amount_of_times, sort_stats_by  # for python 2.x remove this row

            profiled_func = _ProfileFunc(function, sort_stats_by)
            for i in range(amount_of_times):
                retval, ps = profiled_func(*args, **kwargs)
            ps.print_stats()
            return retval  # returns the results of the function
        return wrapper

    if callable(amount_of_times):  # incase you don't want to specify the amount of times
        func = amount_of_times  # amount_of_times is the function in here
        amount_of_times = 5  # the default amount
        return real_decorator(func)
    return real_decorator

Przykład

profilowanie funkcji baz

import time

@cumulative_profiler
def baz():
    time.sleep(1)
    time.sleep(2)
    return 1

baz()

baz pobiegł 5 razy i wydrukował to:

         20 function calls in 15.003 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       10   15.003    1.500   15.003    1.500 {built-in method time.sleep}
        5    0.000    0.000   15.003    3.001 <ipython-input-9-c89afe010372>:3(baz)
        5    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

określając ilość razy

@cumulative_profiler(3)
def baz():
    ...
moshevi
źródło
7

Rozwiązanie tylko dla terminali (i najprostsze), na wypadek, gdyby wszystkie te fantazyjne interfejsy użytkownika nie instalowały się lub nie działały: całkowicie
zignoruj cProfilei zamień na pyinstrument, które będą zbierać i wyświetlać drzewo wywołań zaraz po wykonaniu.

Zainstalować:

$ pip install pyinstrument

Profil i wynik wyświetlania:

$ python -m pyinstrument ./prog.py

Działa z python2 i 3.

[EDYCJA] Dokumentacja API do profilowania tylko części kodu można znaleźć tutaj .

Francois
źródło
6

Moim sposobem jest użycie yappi ( https://github.com/sumerc/yappi ). Jest to szczególnie przydatne w połączeniu z serwerem RPC, w którym (nawet tylko w celu debugowania) rejestrujesz metodę uruchamiania, zatrzymywania i drukowania informacji profilowania, np. W ten sposób:

@staticmethod
def startProfiler():
    yappi.start()

@staticmethod
def stopProfiler():
    yappi.stop()

@staticmethod
def printProfiler():
    stats = yappi.get_stats(yappi.SORTTYPE_TTOT, yappi.SORTORDER_DESC, 20)
    statPrint = '\n'
    namesArr = [len(str(stat[0])) for stat in stats.func_stats]
    log.debug("namesArr %s", str(namesArr))
    maxNameLen = max(namesArr)
    log.debug("maxNameLen: %s", maxNameLen)

    for stat in stats.func_stats:
        nameAppendSpaces = [' ' for i in range(maxNameLen - len(stat[0]))]
        log.debug('nameAppendSpaces: %s', nameAppendSpaces)
        blankSpace = ''
        for space in nameAppendSpaces:
            blankSpace += space

        log.debug("adding spaces: %s", len(nameAppendSpaces))
        statPrint = statPrint + str(stat[0]) + blankSpace + " " + str(stat[1]).ljust(8) + "\t" + str(
            round(stat[2], 2)).ljust(8 - len(str(stat[2]))) + "\t" + str(round(stat[3], 2)) + "\n"

    log.log(1000, "\nname" + ''.ljust(maxNameLen - 4) + " ncall \tttot \ttsub")
    log.log(1000, statPrint)

Następnie, gdy twój program działa, możesz uruchomić startProfilerprofilowanie w dowolnym momencie, wywołując metodę RPC i zrzucić informacje profilowania do pliku dziennika, wywołując printProfiler(lub zmodyfikuj metodę rpc, aby zwrócić ją do programu wywołującego) i uzyskać takie dane wyjściowe:

2014-02-19 16:32:24,128-|SVR-MAIN  |-(Thread-3   )-Level 1000: 
name                                                                                                                                      ncall     ttot    tsub
2014-02-19 16:32:24,128-|SVR-MAIN  |-(Thread-3   )-Level 1000: 
C:\Python27\lib\sched.py.run:80                                                                                                           22        0.11    0.05
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\xmlRpc.py.iterFnc:293                                                22        0.11    0.0
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\serverMain.py.makeIteration:515                                                    22        0.11    0.0
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\PicklingXMLRPC.py._dispatch:66                                       1         0.0     0.0
C:\Python27\lib\BaseHTTPServer.py.date_time_string:464                                                                                    1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\_psmswindows.py._get_raw_meminfo:243     4         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.decode_request_content:537                                                                          1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\_psmswindows.py.get_system_cpu_times:148 4         0.0     0.0
<string>.__new__:8                                                                                                                        220       0.0     0.0
C:\Python27\lib\socket.py.close:276                                                                                                       4         0.0     0.0
C:\Python27\lib\threading.py.__init__:558                                                                                                 1         0.0     0.0
<string>.__new__:8                                                                                                                        4         0.0     0.0
C:\Python27\lib\threading.py.notify:372                                                                                                   1         0.0     0.0
C:\Python27\lib\rfc822.py.getheader:285                                                                                                   4         0.0     0.0
C:\Python27\lib\BaseHTTPServer.py.handle_one_request:301                                                                                  1         0.0     0.0
C:\Python27\lib\xmlrpclib.py.end:816                                                                                                      3         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.do_POST:467                                                                                         1         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.is_rpc_path_valid:460                                                                               1         0.0     0.0
C:\Python27\lib\SocketServer.py.close_request:475                                                                                         1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\__init__.py.cpu_times:1066               4         0.0     0.0 

Może to nie być bardzo przydatne w przypadku krótkich skryptów, ale pomaga zoptymalizować procesy typu serwerowego, szczególnie biorąc pod uwagę, że printProfilermetoda może być wywoływana wiele razy w czasie w celu profilowania i porównania np. Różnych scenariuszy użycia programu.

W nowszych wersjach yappi działa następujący kod:

@staticmethod
def printProfile():
    yappi.get_func_stats().print_all()
Pan Girgitt
źródło
Czy nie należy nazywać go Głupim Yappi?
Therealstubot
Niestety powyższy kod działa tylko z wersją 0.62, która nie jest dostępna w pypy. Moduł musi zostać skompilowany ze źródeł 0.62 dostępnych tutaj: github.com/nirs/yappi/releases lub użyj kompilacji, którą wykonałem dla Windows w repo rozwidlonym w tym celu github.com/Girgitt/yappi/releases
Pan Girgitt
kompatybilność z wersją 1.0 można łatwo zapewnić - przynajmniej w przypadku wydruku - modyfikując funkcję printProfiler: def printProfiler(): if not yappi_available: return stats = yappi.get_func_stats() stats.print_all(columns={0:("name",90), 1:("ncall", 5), 2:("tsub", 8), 3:("ttot", 8), 4:("tavg",8)}) (OK po kilkakrotnym włożeniu bloku kodu do komentarza, którego zrezygnowałem. Jest to niewiarygodnie trudne dla strony z pytaniami i zadaniami programowymi. )
Pan Girgitt,
4

Nowym narzędziem do obsługi profilowania w Pythonie jest PyVmMonitor: http://www.pyvmmonitor.com/

Ma kilka unikalnych funkcji, takich jak

  • Dołącz profiler do działającego programu (CPython)
  • Profilowanie na żądanie z integracją Yappi
  • Profil na innej maszynie
  • Obsługa wielu procesów (multiprocessing, django ...)
  • Podgląd próbkowania na żywo / widok procesora (z wyborem zakresu czasowego)
  • Profilowanie deterministyczne poprzez integrację cProfile / profilu
  • Przeanalizuj istniejące wyniki PStats
  • Otwórz pliki DOT
  • Programowy dostęp do interfejsu API
  • Grupuj próbki według metody lub linii
  • Integracja PyDev
  • Integracja z PyCharm

Uwaga: jest komercyjny, ale darmowy dla open source.

Fabio Zadrozny
źródło
4

gprof2dot_magic

Magiczna funkcja gprof2dotdo profilowania dowolnej instrukcji Python jako wykresu DOT w JupyterLab lub Jupyter Notebook.

wprowadź opis zdjęcia tutaj

Repozytorium GitHub: https://github.com/mattijn/gprof2dot_magic

instalacja

Upewnij się, że masz pakiet Python gprof2dot_magic.

pip install gprof2dot_magic

Jego zależności gprof2dotrównież graphvizzostaną zainstalowane

stosowanie

Aby włączyć funkcję magiczną, najpierw załaduj gprof2dot_magicmoduł

%load_ext gprof2dot_magic

a następnie profilować dowolną instrukcję liniową jako wykres DOT jako taki:

%gprof2dot print('hello world')

wprowadź opis zdjęcia tutaj

Mattijn
źródło
3

Czy kiedykolwiek chciałeś wiedzieć, co do cholery robi ten skrypt Pythona? Wprowadź powłokę inspekcyjną. Inspect Shell pozwala drukować / zmieniać globale i uruchamiać funkcje bez przerywania uruchomionego skryptu. Teraz z autouzupełnianiem i historią poleceń (tylko w systemie Linux).

Inspect Shell nie jest debuggerem w stylu pdb.

https://github.com/amoffat/Inspect-Shell

Możesz użyć tego (i swojego zegarka).

Pułkownik Panika
źródło
3

Zależy to od tego, co chcesz zobaczyć w profilowaniu. Proste pomiary czasu mogą być podane przez (bash).

time python python_prog.py

Nawet „/ usr / bin / time” może generować szczegółowe metryki przy użyciu flagi „--verbose”.

Aby sprawdzić metryki czasu podane przez każdą funkcję i lepiej zrozumieć, ile czasu spędza się na funkcjach, możesz użyć wbudowanego cProfile w pythonie.

Przechodząc do bardziej szczegółowych wskaźników, takich jak skuteczność, czas nie jest jedyną miarą. Możesz martwić się pamięcią, wątkami itp.
Opcje profilowania:
1. line_profiler to kolejny profiler używany powszechnie do wyszukiwania metryk synchronizacji linia po linii.
2. memory_profiler to narzędzie do profilowania użycia pamięci.
3. heapy (z projektu Guppy) Profil, w jaki sposób wykorzystywane są obiekty w stercie.

Są to niektóre z typowych, których używam. Ale jeśli chcesz dowiedzieć się więcej, spróbuj przeczytać tę książkę. Jest to całkiem dobra książka na temat rozpoczynania pracy z myślą o wydajności. Możesz przejść do zaawansowanych tematów dotyczących używania Pythona skompilowanego Cython i JIT (Just-in-time).

VishalMishra
źródło
2

Dzięki profilerowi statystycznemu, takiemu jak austin , nie jest wymagana żadna instrumentacja, co oznacza, że ​​dane profilowania można uzyskać z aplikacji Python po prostu za pomocą

austin python3 my_script.py

Dane wyjściowe surowe nie są zbyt przydatne, ale możesz przesłać je do flamegraph.pl, aby uzyskać graficzną reprezentację tych danych, która daje podział na to, gdzie czas (mierzony w mikrosekundach czasu rzeczywistego) jest spędzany.

austin python3 my_script.py | flamegraph.pl > my_script_profile.svg
Phoenix87
źródło
1

Istnieje również profiler statystyczny o nazwie statprof. Jest to profiler do próbkowania, więc dodaje minimalny narzut do twojego kodu i zapewnia czasy taktowania oparte na linii (nie tylko funkcji). Jest bardziej odpowiedni dla miękkich aplikacji w czasie rzeczywistym, takich jak gry, ale może być mniej precyzyjny niż cProfile.

Wersja PyPI jest nieco stary, więc można go zainstalować z pipokreślając GIT repozytorium :

pip install git+git://github.com/bos/statprof.py@1a33eba91899afe17a8b752c6dfdec6f05dd0c01

Możesz uruchomić go w następujący sposób:

import statprof

with statprof.profile():
    my_questionable_function()

Zobacz także https://stackoverflow.com/a/10333592/320036

z0r
źródło
1

Właśnie opracowałem własny profiler zainspirowany pypref_time:

https://github.com/modaresimr/auto_profiler

Dodanie dekoratora spowoduje wyświetlenie drzewa czasochłonnych funkcji

@Profiler(depth=4, on_disable=show)

Install by: pip install auto_profiler

Przykład

import time # line number 1
import random

from auto_profiler import Profiler, Tree

def f1():
    mysleep(.6+random.random())

def mysleep(t):
    time.sleep(t)

def fact(i):
    f1()
    if(i==1):
        return 1
    return i*fact(i-1)


def show(p):
    print('Time   [Hits * PerHit] Function name [Called from] [Function Location]\n'+\
          '-----------------------------------------------------------------------')
    print(Tree(p.root, threshold=0.5))

@Profiler(depth=4, on_disable=show)
def main():
    for i in range(5):
        f1()

    fact(3)


if __name__ == '__main__':
    main()

Przykładowy wynik


Time   [Hits * PerHit] Function name [Called from] [function location]
-----------------------------------------------------------------------
8.974s [1 * 8.974]  main  [auto-profiler/profiler.py:267]  [/test/t2.py:30]
├── 5.954s [5 * 1.191]  f1  [/test/t2.py:34]  [/test/t2.py:14]
   └── 5.954s [5 * 1.191]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
       └── 5.954s [5 * 1.191]  <time.sleep>
|
|
|   # The rest is for the example recursive function call fact
└── 3.020s [1 * 3.020]  fact  [/test/t2.py:36]  [/test/t2.py:20]
    ├── 0.849s [1 * 0.849]  f1  [/test/t2.py:21]  [/test/t2.py:14]
       └── 0.849s [1 * 0.849]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
           └── 0.849s [1 * 0.849]  <time.sleep>
    └── 2.171s [1 * 2.171]  fact  [/test/t2.py:24]  [/test/t2.py:20]
        ├── 1.552s [1 * 1.552]  f1  [/test/t2.py:21]  [/test/t2.py:14]
           └── 1.552s [1 * 1.552]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
        └── 0.619s [1 * 0.619]  fact  [/test/t2.py:24]  [/test/t2.py:20]
            └── 0.619s [1 * 0.619]  f1  [/test/t2.py:21]  [/test/t2.py:14]
Ali
źródło
0

Kiedy nie jestem rootem na serwerze, używam lsprofcalltree.py i uruchamiam mój program w następujący sposób:

python lsprofcalltree.py -o callgrind.1 test.py

Następnie mogę otworzyć raport za pomocą dowolnego oprogramowania zgodnego z callgrind, takiego jak qcachegrind

Vincent Fenet
źródło
0

Aby uzyskać szybkie statystyki profilu dla fragmentów kodu w notatniku IPython. Można osadzić line_profiler i memory_profiler bezpośrednio w swoich notatnikach.

Zdobyć!

!pip install line_profiler
!pip install memory_profiler

Załaduj To!

%load_ext line_profiler
%load_ext memory_profiler

Użyj tego!


%czas

%time print('Outputs CPU time,Wall Clock time') 
#CPU times: user 2 µs, sys: 0 ns, total: 2 µs Wall time: 5.96 µs

Daje:

  • Czasy procesora: czas wykonania na poziomie procesora
  • sys times: czas wykonania na poziomie systemu
  • łącznie: czas procesora + czas systemowy
  • Czas naścienny: Czas naścienny

% timeit

%timeit -r 7 -n 1000 print('Outputs execution time of the snippet') 
#1000 loops, best of 7: 7.46 ns per loop
  • Daje najlepszy czas z podanej liczby przebiegów (r) w pętli (n) razy.
  • Wyświetla szczegółowe informacje o buforowaniu systemu:
    • Gdy fragmenty kodu są wykonywane wielokrotnie, system buforuje kilka operacji i nie wykonuje ich ponownie, co może utrudnić dokładność raportów profilu.

% przycinania

%prun -s cumulative 'Code to profile' 

Daje:

  • liczba wywołań funkcji (ncalls)
  • ma wpisy na wywołanie funkcji (odrębne)
  • czas potrzebny na połączenie (percall)
  • czas, który upłynął do wywołania tej funkcji (cumtime)
  • nazwa func / modułu o nazwie etc ...

Profil skumulowany


% memit

%memit 'Code to profile'
#peak memory: 199.45 MiB, increment: 0.00 MiB

Daje:

  • Zużycie pamięci

% lprun

#Example function
def fun():
  for i in range(10):
    print(i)

#Usage: %lprun <name_of_the_function> function
%lprun -f fun fun()

Daje:

  • Statystyki liniowe

LineProfile

Aditya Patnaik
źródło