Wie können Sie ein Python-Skript profilieren?


1283

Project Euler und andere Codierungswettbewerbe haben häufig eine maximale Laufzeit oder die Benutzer geben an, wie schnell ihre jeweilige Lösung ausgeführt wird. Bei Python sind die Ansätze manchmal etwas kludgey - dh das Hinzufügen von Timing-Code zu __main__.

Wie lässt sich feststellen, wie lange die Ausführung eines Python-Programms dauert?


113
Project Euler-Programme sollten keine Profilerstellung benötigen. Entweder haben Sie einen Algorithmus, der in weniger als einer Minute funktioniert, oder Sie haben einen völlig falschen Algorithmus. "Tuning" ist selten angebracht. Sie müssen in der Regel einen neuen Ansatz wählen.
S.Lott

105
S.Lott: Die Profilerstellung ist oft eine hilfreiche Methode, um festzustellen, welche Unterprogramme langsam sind. Subroutinen, die lange dauern, sind großartige Kandidaten für eine algorithmische Verbesserung.
Stalepretzel

Antworten:


1369

Python enthält einen Profiler namens cProfile . Es gibt nicht nur die Gesamtlaufzeit an, sondern auch die Zeiten für jede Funktion separat und gibt an, wie oft jede Funktion aufgerufen wurde, sodass Sie leicht bestimmen können, wo Sie Optimierungen vornehmen sollten.

Sie können es aus Ihrem Code oder vom Interpreter wie folgt aufrufen:

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

Noch nützlicher ist, dass Sie das cProfile aufrufen können, wenn Sie ein Skript ausführen:

python -m cProfile myscript.py

Um es noch einfacher zu machen, habe ich eine kleine Batch-Datei namens 'profile.bat' erstellt:

python -m cProfile %1

Alles was ich tun muss ist laufen:

profile euler048.py

Und ich verstehe das:

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}

BEARBEITEN: Der Link zu einer guten Video-Ressource von PyCon 2013 mit dem Titel Python- Profilerstellung wurde
ebenfalls über YouTube aktualisiert .


251
Es ist auch nützlich, die Ergebnisse zu sortieren, was mit dem Schalter -s erfolgen kann, Beispiel: '-s Zeit'. Sie können die Sortieroptionen kumulativ / Name / Zeit / Datei verwenden.
Jiri

19
Es ist auch erwähnenswert, dass Sie das cProfile-Modul von ipython mit der magischen Funktion% prun (Profillauf) verwenden können. Importieren Sie zuerst Ihr Modul und rufen Sie dann die Hauptfunktion mit% prun auf: import euler048; % prun euler048.main ()
RussellStewart

53
Zur Visualisierung von cProfile-Dumps (erstellt von python -m cProfile -o <out.profile> <script>) wird RunSnakeRun aufgerufen, da dies von runsnake <out.profile>unschätzbarem Wert ist.
Lily Chung

13
@NeilG auch für Python 3, wird cprofileempfohlen, immer noch über profile.
Trichoplax

17
Zur Visualisierung von cProfile-Dumps wurde RunSnakeRun seit 2011 nicht mehr aktualisiert und unterstützt Python3 nicht. Sie sollten stattdessen snakeviz verwenden
Giacomo Tecya Pigani

423

Vor einiger Zeit habe ich gemacht pycallgraph, die eine Visualisierung aus Ihrem Python-Code generiert. Bearbeiten: Ich habe das Beispiel aktualisiert, um mit 3.3 zu arbeiten, der neuesten Version zum Zeitpunkt dieses Schreibens.

Nach dem pip install pycallgraphInstallieren von GraphViz können Sie es über die Befehlszeile ausführen:

pycallgraph graphviz -- ./mypythonscript.py

Oder Sie können bestimmte Teile Ihres Codes profilieren:

from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput

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

Beides generiert eine pycallgraph.pngDatei ähnlich dem folgenden Bild:

Geben Sie hier die Bildbeschreibung ein


43
Färben Sie basierend auf der Anzahl der Anrufe? In diesem Fall sollten Sie die Farbe basierend auf der Zeit festlegen, da die Funktion mit den meisten Aufrufen nicht immer diejenige ist, die die meiste Zeit benötigt.
rot

21
@red Sie können Farben nach Belieben und sogar unabhängig für jede Messung anpassen. Zum Beispiel rot für Anrufe, blau für die Zeit, grün für die Speichernutzung.
Gak

2
diesen Fehler bekommenTraceback (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
Ich habe dies aktualisiert, um zu erwähnen, dass Sie GraphViz installieren müssen, damit die Dinge wie beschrieben funktionieren. Unter Ubuntu ist das nur so sudo apt-get install graphviz.
mlissner

2
Die Installation erfordert ein wenig Arbeit. Hier sind 3 Schritte hilfreich. 1. Installieren Sie über pip, 2. Installieren Sie GraphViz über exe. 3. Richten Sie Pfadvariablen zum GraphViz-Verzeichnis ein. 4. Finden Sie heraus, wie Sie alle anderen Fehler beheben können. 5. Finden Sie heraus, wo die PNG-Datei gespeichert wird?
Sumpf

199

Es ist erwähnenswert, dass die Verwendung des Profilers (standardmäßig) nur für den Hauptthread funktioniert und Sie keine Informationen von anderen Threads erhalten, wenn Sie diese verwenden. Dies kann ein kleines Problem sein, da es in der Profiler-Dokumentation völlig unerwähnt ist .

Wenn Sie auch Threads profilieren möchten, sollten Sie sich die threading.setprofile()Funktion in den Dokumenten ansehen .

Sie können dazu auch eine eigene threading.ThreadUnterklasse erstellen :

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,))

und verwenden Sie diese ProfiledThreadKlasse anstelle der Standardklasse. Es gibt Ihnen vielleicht mehr Flexibilität, aber ich bin mir nicht sicher, ob es sich lohnt, insbesondere wenn Sie Code von Drittanbietern verwenden, der Ihre Klasse nicht verwenden würde.


1
Ich sehe auch keinen Hinweis auf runcall in der Dokumentation. Wenn ich mir cProfile.py anschaue, bin ich mir nicht sicher, warum Sie die Funktion threading.Thread.run oder self als Argument verwenden. Ich hätte erwartet, hier einen Verweis auf die Ausführungsmethode eines anderen Threads zu sehen .
PypeBros

Es ist nicht in der Dokumentation, aber es ist im Modul. Siehe hg.python.org/cpython/file/6bf07db23445/Lib/cProfile.py#l140 . Auf diese Weise können Sie einen bestimmten Funktionsaufruf profilieren. In unserem Fall möchten wir die targetFunktion des Threads profilieren , die der threading.Thread.run()Aufruf ausführt. Aber wie ich in der Antwort sagte, lohnt es sich wahrscheinlich nicht, Thread zu unterordnen, da Code von Drittanbietern ihn nicht verwendet und stattdessen verwendet threading.setprofile().
Joe Shaw

9
Das Umschließen des Codes mit profiler.enable () und profiler.disable () scheint ebenfalls recht gut zu funktionieren. Das ist im Grunde das, was Runcall macht und es erzwingt keine Anzahl von Argumenten oder ähnlichen Dingen.
PypeBros

1
Ich habe meinen eigenen stackoverflow.com/questions/10748118/… mit ddaa.net/blog/python/lsprof-calltree kombiniert und es funktioniert irgendwie ;! -)
Dima Tisnek

1
Joe, weißt du, wie der Profiler in Python 3.4 mit Asyncio spielt?
Nick Chammas

149

Das Python-Wiki ist eine großartige Seite für die Profilerstellung von Ressourcen: http://wiki.python.org/moin/PythonSpeed/PerformanceTips#Profiling_Code

ebenso wie die Python-Dokumente: http://docs.python.org/library/profile.html

Wie von Chris Lawlor gezeigt, ist cProfile ein großartiges Werkzeug und kann leicht zum Drucken auf dem Bildschirm verwendet werden:

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

oder zu archivieren:

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

PS> Wenn Sie Ubuntu verwenden, stellen Sie sicher, dass Sie das Python-Profil installieren

apt-get install python-profiler 

Wenn Sie in eine Datei ausgeben, können Sie mit den folgenden Tools schöne Visualisierungen erhalten

PyCallGraph: ein Werkzeug Aufrufgraphen Bilder erstellen zu
installieren:

 pip install pycallgraph

Lauf:

 pycallgraph mine.py args

Aussicht:

 gimp pycallgraph.png

Sie können verwenden, was Sie möchten, um die PNG-Datei anzuzeigen, ich habe Gimp verwendet
Leider bekomme ich oft

Punkt: Die Grafik ist zu groß für Kairo-Renderer-Bitmaps. Skalierung um 0,257079 passend

Das macht meine Bilder ungewöhnlich klein. Also erstelle ich im Allgemeinen SVG-Dateien:

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

PS> Stellen Sie sicher, dass Sie graphviz installieren (das das Punktprogramm bereitstellt):

pip install graphviz

Alternative Grafik mit gprof2dot über @maxy / @quodlibetor:

pip install gprof2dot
python -m cProfile -o profile.pstats mine.py
gprof2dot -f pstats profile.pstats | dot -Tsvg -o mine.svg

12
gprof2dot kann diese Grafiken auch erstellen . Ich denke, die Ausgabe ist etwas schöner ( Beispiel ).
Maxy

2
graphviz ist auch erforderlich, wenn Sie OSX
Vaibhav Mishra

134

@ Maxys Kommentar zu dieser Antwort hat mir so geholfen, dass ich denke, dass sie eine eigene Antwort verdient: Ich hatte bereits von cProfile generierte .pstats-Dateien und wollte die Dinge nicht mit pycallgraph erneut ausführen, also habe ich gprof2dot verwendet und bin hübsch geworden 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

und BLAM!

Es wird Punkt verwendet (das gleiche, was Pycallgraph verwendet), sodass die Ausgabe ähnlich aussieht. Ich habe den Eindruck, dass gprof2dot weniger Informationen verliert:

gprof2dot Beispielausgabe


1
Guter Ansatz, funktioniert sehr gut, da Sie SVG in Chrome usw. anzeigen und vergrößern / verkleinern können. Die dritte Zeile enthält Tippfehler und sollte lauten: ln -s pwd/gprof2dot/gprof2dot.py $ HOME / bin (oder verwenden Sie in den meisten Shells ln -s $ PWD / gprof2dot / gprof2dot.py ~ / bin - der schwerwiegende Akzent wird zuerst als Formatierung verwendet Ausführung).
RichVel

2
Ah, guter Punkt. Ich verstehe lndie Argumentationsreihenfolge fast jedes Mal falsch.
Quodlibetor

7
Der Trick besteht darin, sich daran zu erinnern, dass ln und cp dieselbe Argumentreihenfolge haben - stellen Sie sich vor, Sie kopieren Datei1 in Datei2 oder Verzeichnis2, stellen jedoch einen Link her
RichVel

Das macht Sinn, ich denke die Verwendung von "TARGET" in der Manpage wirft mich um.
Quodlibetor

Bitte, wie sind Sie zu abgerundeten Ecken gekommen? Ich denke, es verbessert die Lesbarkeit. Ich bekomme nur hässliche scharfe Ecken, die bei vielen Kanten um Kisten nicht cool sind.
Hibou57

78

Bei der Recherche zu diesem Thema bin ich auf ein praktisches Tool namens SnakeViz gestoßen . SnakeViz ist ein webbasiertes Tool zur Visualisierung von Profilen. Es ist sehr einfach zu installieren und zu verwenden. Die übliche Art, wie ich es benutze, besteht darin, eine %prunStatistikdatei mit SnakeViz zu generieren und diese dann zu analysieren.

Die hauptsächlich verwendete Technik ist das unten gezeigte Sunburst-Diagramm , in dem die Hierarchie der Funktionsaufrufe als Schichten von Bögen und Zeitinformationen angeordnet ist, die in ihren Winkelbreiten codiert sind.

Das Beste ist, dass Sie mit dem Diagramm interagieren können. Zum Vergrößern kann man beispielsweise auf einen Bogen klicken, und der Bogen und seine Nachkommen werden als neuer Sunburst vergrößert, um weitere Details anzuzeigen.

Geben Sie hier die Bildbeschreibung ein


1
Die Antwort von CodeCabbie enthält die (kurzen) Installationsanweisungen und zeigt, wie SnakeViz (einfach) verwendet wird.
Oren Milman

Hier habe ich IMHO gute Anleitung gelesen, wie man Profiling für Python auf Jupyter Notebook verwendet: Richtung Dataascience.com/speed-up-jupyter-notebooks-20716cbe2025
Alexei Martianov

73

Einfachster und schnellster Weg, um herauszufinden, wohin die ganze Zeit geht.

1. pip install snakeviz

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

3. snakeviz temp.dat

Zeichnet ein Kreisdiagramm in einem Browser. Das größte Stück ist die Problemfunktion. Sehr einfach.


1
Das war sehr hilfreich. Vielen Dank.
jippyjoe4

55

Ich denke, das cProfileist großartig für die Profilerstellung, während kcachegrindes großartig ist, um die Ergebnisse zu visualisieren. Das pyprof2calltreeDazwischen übernimmt die Dateikonvertierung.

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

So installieren Sie die erforderlichen Tools (zumindest unter Ubuntu):

apt-get install kcachegrind
pip install pyprof2calltree

Das Ergebnis:

Screenshot des Ergebnisses


9
Mac-Benutzer installieren brew install qcachegrindund ersetzen jeweils kcachegrind mit qcachegrind in der Beschreibung für eine erfolgreiche Profilerstellung.
Kevin Katzke

Ich musste dies tun, um es zum export QT_X11_NO_MITSHM=1
Laufen

41

Erwähnenswert ist auch der GUI cProfile Dump Viewer RunSnakeRun . Sie können damit sortieren und auswählen und so die relevanten Teile des Programms vergrößern. Die Größe der Rechtecke im Bild ist proportional zur benötigten Zeit. Wenn Sie mit der Maus über ein Rechteck fahren, wird dieser Aufruf in der Tabelle und überall auf der Karte hervorgehoben. Wenn Sie auf ein Rechteck doppelklicken, wird dieser Teil vergrößert. Es zeigt Ihnen, wer diesen Teil anruft und wie dieser Teil anruft.

Die beschreibenden Informationen sind sehr hilfreich. Es zeigt Ihnen den Code für dieses Bit, der hilfreich sein kann, wenn Sie mit integrierten Bibliotheksaufrufen arbeiten. Hier erfahren Sie, in welcher Datei und in welcher Zeile Sie den Code finden.

Ich möchte auch darauf hinweisen, dass das OP "Profiling" sagte, aber es scheint, dass er "Timing" meinte. Beachten Sie, dass Programme beim Profilieren langsamer ausgeführt werden.

Geben Sie hier die Bildbeschreibung ein


34

Ein nettes Profiling-Modul ist der line_profiler (wird mit dem Skript kernprof.py aufgerufen). Es kann hier heruntergeladen werden .

Nach meinem Verständnis gibt cProfile nur Informationen über die Gesamtzeit, die in jeder Funktion verbracht wurde. Einzelne Codezeilen sind also nicht zeitgesteuert. Dies ist ein Problem im wissenschaftlichen Rechnen, da oft eine einzelne Zeile viel Zeit in Anspruch nehmen kann. Wie ich mich erinnere, hat cProfile auch nicht die Zeit erfasst, die ich in "numpy.dot" verbracht habe.


34

Ich habe kürzlich Thunfisch zur Visualisierung der Python-Laufzeit und zum Importieren von Profilen erstellt. Dies kann hier hilfreich sein.

Geben Sie hier die Bildbeschreibung ein

Installieren mit

pip install tuna

Erstellen Sie ein Laufzeitprofil

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

oder ein Importprofil (Python 3.7+ erforderlich)

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

Führen Sie dann einfach Thunfisch für die Datei aus

tuna program.prof

33

Profil

line_profiler(hier bereits vorgestellt) auch inspiriert pprofile, was beschrieben wird als:

Liniengranularität, threadbewusster deterministischer und statistischer Pure-Python-Profiler

Es bietet Zeilengranularität, da line_profileres reines Python ist, als eigenständiger Befehl oder als Modul verwendet werden kann und sogar Dateien im Callgrind-Format generieren kann, mit denen es einfach analysiert werden kann [k|q]cachegrind.

vprof

Es gibt auch vprof , ein Python-Paket, das wie folgt beschrieben wird:

[...] Bereitstellung umfangreicher und interaktiver Visualisierungen für verschiedene Python-Programmmerkmale wie Laufzeit und Speichernutzung.

Heatmap


14

Es gibt viele gute Antworten, aber sie verwenden entweder die Befehlszeile oder ein externes Programm zum Profilieren und / oder Sortieren der Ergebnisse.

Ich habe wirklich eine Möglichkeit verpasst, die ich in meiner IDE (eclipse-PyDev) verwenden konnte, ohne die Befehlszeile zu berühren oder etwas zu installieren. Hier ist es also.

Profilerstellung ohne Kommandozeile

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)

Weitere Informationen finden Sie in den Dokumenten oder anderen Antworten.


Das Profil druckt beispielsweise {map} oder {xxx}. Woher weiß ich, dass die Methode {xxx} aus welcher Datei aufgerufen wird? Mein Profil druckt {Methode 'compress' von 'zlib.Compress' -Objekten} nimmt die meiste Zeit in Anspruch, aber ich verwende keine zlib, daher kann es sein, dass eine Call-Numpy-Funktion sie verwendet. Woher weiß ich, welche Datei und welche Zeile genau benötigt werden?
machen

12

Nach der Antwort von Joe Shaw, dass Multithread-Code nicht wie erwartet funktioniert, stellte ich fest, dass die runcallMethode in cProfile lediglich den profilierten Funktionsaufruf ausführt self.enable()und self.disable()aufruft, sodass Sie dies einfach selbst tun und den gewünschten Code dazwischen haben können minimale Störung des vorhandenen Codes.


3
Hervorragender Tipp! Ein kurzer Blick auf cprofile.pyden Quellcode zeigt, dass genau das der runcall()Fall ist. Spezifischere sein, nachdem sie mit einem Profil Instanz prof = cprofile.Profile(), sofort anrufen prof.disable(), und dann fügen Sie einfach prof.enable()und prof.disable()Anrufe rund um den Abschnitt des Codes Sie profiliert werden soll.
Martineau

Dies ist sehr hilfreich, aber es scheint, dass der Code, der sich tatsächlich zwischen Aktivieren und Deaktivieren befindet, nicht profiliert ist - nur die Funktionen, die er aufruft. Habe ich das richtig Ich müsste diesen Code in einen Funktionsaufruf einschließen, damit er für eine der Zahlen in print_stats () zählt.
Bob Stein

10

In Virtaals Quelle gibt es eine sehr nützliche Klasse und einen Dekorator, die das Profiling (auch für bestimmte Methoden / Funktionen) sehr einfach machen können. Die Ausgabe kann dann in KCacheGrind sehr bequem angezeigt werden.


1
Vielen Dank für dieses Juwel. Zu Ihrer Information: Dies kann als eigenständiges Modul mit jedem Code verwendet werden. Eine Virtaal-Codebasis ist nicht erforderlich. Speichern Sie einfach die Datei in profiling.py und importieren Sie profile_func (). Verwenden Sie @profile_func () als Dekorateur für alle Funktionen, die Sie zum Profilieren und Bratschen benötigen. :)
Amjith

9

cProfile eignet sich hervorragend für die schnelle Profilerstellung, aber die meiste Zeit endete es für mich mit den Fehlern. Die Funktion runctx löst dieses Problem, indem sie die Umgebung und die Variablen korrekt initialisiert. Ich hoffe, sie kann für jemanden nützlich sein:

import cProfile
cProfile.runctx('foo()', None, locals())

7

Wenn Sie einen kumulativen Profiler erstellen möchten, dh die Funktion mehrmals hintereinander ausführen und die Summe der Ergebnisse anzeigen möchten.

Sie können diesen cumulative_profilerDekorateur verwenden:

Es ist Python> = 3.6-spezifisch, aber Sie können es entfernen, nonlocaldamit es auf älteren Versionen funktioniert.

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

Beispiel

Profilerstellung der Funktion baz

import time

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

baz()

baz lief 5 mal und druckte dies:

         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}

Angabe der Häufigkeit

@cumulative_profiler(3)
def baz():
    ...

7

Die reine (und einfachste) Lösung für den Fall, dass all diese ausgefallenen Benutzeroberflächen nicht installiert oder ausgeführt werden können:
Ignorieren Sie sie cProfilevollständig und ersetzen Sie sie durch pyinstrument, um den Aufrufbaum direkt nach der Ausführung zu erfassen und anzuzeigen.

Installieren:

$ pip install pyinstrument

Profil und Anzeigeergebnis:

$ python -m pyinstrument ./prog.py

Funktioniert mit Python2 und 3.

[EDIT] Die Dokumentation der API zum Profilieren nur eines Teils des Codes finden Sie hier .


6

Ich verwende yappi ( https://github.com/sumerc/yappi ). Dies ist besonders nützlich in Kombination mit einem RPC-Server, auf dem Sie (auch nur zum Debuggen) eine Methode zum Starten, Stoppen und Drucken von Profilinformationen registrieren, z. B. auf folgende Weise:

@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)

Wenn Ihr Programm funktioniert, können Sie den Profiler jederzeit starten, indem Sie die startProfilerRPC-Methode aufrufen und Profilinformationen durch Aufrufen in eine Protokolldatei printProfilerkopieren (oder die rpc-Methode ändern, um sie an den Aufrufer zurückzugeben), und folgende Ausgabe erhalten:

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 

Es ist möglicherweise nicht sehr nützlich für kurze Skripte, hilft jedoch bei der Optimierung von Prozessen vom Servertyp, insbesondere da die printProfilerMethode im Laufe der Zeit mehrmals aufgerufen werden kann, um z. B. verschiedene Programmnutzungsszenarien zu profilieren und zu vergleichen.

In neueren Versionen von yappi funktioniert der folgende Code:

@staticmethod
def printProfile():
    yappi.get_func_stats().print_all()

Sollte es nicht der erstaunliche Yappi genannt werden?
Therealstubot

Leider funktioniert der obige Code nur mit Version 0.62, die auf pypy nicht verfügbar ist. Das Modul muss aus 0,62 Quellen zusammengestellt werden, die hier verfügbar sind: github.com/nirs/yappi/releases oder verwenden Sie den Build, den ich für Windows in Repo-
Forked

Die Kompatibilität mit Version 1.0 kann - zumindest für die Druckausgabe - einfach durch Ändern der printProfiler-Funktion bereitgestellt werden: 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, nachdem ich einige Male versucht habe, einen Codeblock in den Kommentar einzufügen, den ich aufgegeben habe. Dies ist für eine programmierorientierte Q & A-Site unglaublich schwierig. )
Herr Girgitt

4

Ein neues Tool zur Profilerstellung in Python ist PyVmMonitor: http://www.pyvmmonitor.com/

Es hat einige einzigartige Funktionen wie

  • Hängen Sie den Profiler an ein laufendes Programm (CPython) an
  • On-Demand-Profilerstellung mit Yappi-Integration
  • Profil auf einer anderen Maschine
  • Unterstützung mehrerer Prozesse (Multiprocessing, Django ...)
  • Live-Sampling / CPU-Ansicht (mit Zeitbereichsauswahl)
  • Deterministische Profilerstellung durch cProfile / Profil-Integration
  • Analysieren Sie vorhandene PStats-Ergebnisse
  • Öffnen Sie DOT-Dateien
  • Programmatischer API-Zugriff
  • Gruppieren Sie die Stichproben nach Methode oder Zeile
  • PyDev-Integration
  • PyCharm-Integration

Hinweis: Es ist kommerziell, aber kostenlos für Open Source.


4

gprof2dot_magic

Magische Funktion gprof2dotzum Profilieren einer Python-Anweisung als DOT-Diagramm in JupyterLab oder Jupyter Notebook.

Geben Sie hier die Bildbeschreibung ein

GitHub-Repo: https://github.com/mattijn/gprof2dot_magic

Installation

Stellen Sie sicher, dass Sie das Python-Paket haben gprof2dot_magic.

pip install gprof2dot_magic

Seine Abhängigkeiten gprof2dotund graphvizwerden ebenfalls installiert

Verwendungszweck

Laden Sie zuerst das gprof2dot_magicModul, um die magische Funktion zu aktivieren

%load_ext gprof2dot_magic

und profilieren Sie dann eine beliebige Zeilenanweisung als DOT-Diagramm als solches:

%gprof2dot print('hello world')

Geben Sie hier die Bildbeschreibung ein


3

Wollten Sie schon immer wissen, was zum Teufel dieses Python-Skript macht? Rufen Sie die Inspect Shell auf. Mit Inspect Shell können Sie Globals drucken / ändern und Funktionen ausführen, ohne das ausgeführte Skript zu unterbrechen. Jetzt mit automatischer Vervollständigung und Befehlsverlauf (nur unter Linux).

Inspect Shell ist kein Debugger im PDF-Stil.

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

Sie könnten das (und Ihre Armbanduhr) verwenden.



3

Dies hängt davon ab, was Sie bei der Profilerstellung sehen möchten. Einfache Zeitmetriken können durch (bash) angegeben werden.

time python python_prog.py

Sogar '/ usr / bin / time' kann mithilfe des Flags '--verbose' detaillierte Metriken ausgeben.

Um die von jeder Funktion angegebenen Zeitmetriken zu überprüfen und besser zu verstehen, wie viel Zeit für Funktionen aufgewendet wird, können Sie das in Python integrierte cProfile verwenden.

Bei detaillierteren Metriken wie der Leistung ist die Zeit nicht die einzige Metrik. Sie können sich Gedanken über Speicher, Threads usw. machen.
Profiling-Optionen:
1. line_profiler ist ein weiterer Profiler, der häufig verwendet wird, um Timing-Metriken zeilenweise herauszufinden.
2. memory_profiler ist ein Tool zum Profilieren der Speichernutzung.
3. Heapy (aus dem Projekt Guppy) Profilieren Sie, wie Objekte im Heap verwendet werden.

Dies sind einige der häufigsten, die ich benutze. Wenn Sie jedoch mehr erfahren möchten, lesen Sie dieses Buch. Es ist ein ziemlich gutes Buch, wenn Sie mit Blick auf die Leistung beginnen. Sie können zu fortgeschrittenen Themen über die Verwendung von Cython und JIT (Just-in-Time) kompiliertem Python übergehen.


2

Mit einem statistischen Profiler wie austin ist keine Instrumentierung erforderlich, sodass Sie Profildaten aus einer Python-Anwendung einfach mit abrufen können

austin python3 my_script.py

Die Rohausgabe ist nicht sehr nützlich, aber Sie können sie an flamegraph.pl weiterleiten , um eine Flammengraphendarstellung dieser Daten zu erhalten, aus der hervorgeht, wo die Zeit (gemessen in Mikrosekunden in Echtzeit) verbracht wird.

austin python3 my_script.py | flamegraph.pl > my_script_profile.svg

2

Zum schnellen Abrufen von Profilstatistiken für Ihre Codefragmente auf einem IPython-Notizbuch. Man kann line_profiler und memory_profiler direkt in ihre Notizbücher einbetten.

Kapiert!

!pip install line_profiler
!pip install memory_profiler

Lade es!

%load_ext line_profiler
%load_ext memory_profiler

Benutze es!


%Zeit

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

Gibt:

  • CPU-Zeiten: Ausführungszeit auf CPU-Ebene
  • sys times: Ausführungszeit auf Systemebene
  • gesamt: CPU-Zeit + Systemzeit
  • Wandzeit: Wanduhrzeit

% timeit

%timeit -r 7 -n 1000 print('Outputs execution time of the snippet') 
#1000 loops, best of 7: 7.46 ns per loop
  • Gibt die beste Zeit aus der angegebenen Anzahl von Läufen (r) in Schleifenzeiten (n).
  • Gibt Details zum System-Caching aus:
    • Wenn Code-Snippets mehrmals ausgeführt werden, speichert das System einige Operationen im Cache und führt sie nicht erneut aus, was die Genauigkeit der Profilberichte beeinträchtigen kann.

% beschneiden

%prun -s cumulative 'Code to profile' 

Gibt:

  • Anzahl der Funktionsaufrufe (ncalls)
  • hat Einträge pro Funktionsaufruf (verschieden)
  • Zeit pro Anruf (percall)
  • Zeit bis zu diesem Funktionsaufruf (cumtime)
  • Name der Funktion / des Moduls mit dem Namen etc ...

Kumulatives Profil


% memit

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

Gibt:

  • Speichernutzung

% lprun

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

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

Gibt:

  • Linienweise Statistiken

LineProfile


1

Es gibt auch einen statistischen Profiler namens statprof. Es ist ein Sampling-Profiler, der Ihrem Code nur minimalen Overhead hinzufügt und zeilenbasierte (nicht nur funktionsbasierte) Timings liefert. Es eignet sich eher für weiche Echtzeitanwendungen wie Spiele, ist jedoch möglicherweise weniger präzise als cProfile.

Die Version in pypi ist ein bisschen alt, kann es so installieren , um mit pipdurch die Angabe der git - Repository :

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

Sie können es so ausführen:

import statprof

with statprof.profile():
    my_questionable_function()

Siehe auch https://stackoverflow.com/a/10333592/320036


1

Ich habe gerade meinen eigenen Profiler entwickelt, der von pypref_time inspiriert ist:

https://github.com/modaresimr/auto_profiler

Durch Hinzufügen eines Dekorateurs wird ein Baum zeitaufwändiger Funktionen angezeigt

@Profiler(depth=4, on_disable=show)

Install by: pip install auto_profiler

Beispiel

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()

Beispielausgabe


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]

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.