Verwendung von cProfile zum Profilieren von Python-Code
Python ist vielleicht nicht die schnellste Sprache, aber es ist oft schnell genug. Und Python ist ideal, wenn die Programmierzeit wichtiger ist als die CPU-Zeit.
Das heißt, wenn eine bestimmte Python-App verzögert ist, müssen Sie sie nicht einfach aufsaugen. Die in der Standardinstallation des Python-Interpreters enthaltenen Tools können Ihnen detailliertes Feedback darüber geben, welche Teile Ihres Programms langsam sind, und einige Hinweise dazu geben, wie Sie sie beschleunigen können.
Verwendung von cProfile
Das cProfile
Modul sammelt Statistiken über die Ausführungszeit eines Python-Programms. Es kann über alles von der gesamten App bis zu einer einzelnen Anweisung oder einem Ausdruck berichten.
Hier ist ein Spielzeugbeispiel für die Verwendung cProfile
:
def add (x, y): x + = str (y) return x def add_2 (x, y): wenn y% 20000 == 0: z = [] für q im Bereich (0,400000): z.append ( q) def main (): a = [] für n im Bereich (0,200000): addiere (a, n) add_2 (a, n) wenn __name__ == '__main__': importiere cProfile cProfile.run ('main ( ) ')
In diesem Beispiel wird die main()
Funktion der Anwendung ausgeführt und die Leistung main()
aller main()
Aufrufe analysiert . Es ist auch möglich, nur einen Teil eines Programms zu analysieren. Am häufigsten wird jedoch zunächst das Profil des gesamten Programms verwendet.
Führen Sie das obige Beispiel aus, und Sie werden mit der folgenden Ausgabe begrüßt:

Was hier gezeigt wird, ist eine Liste aller vom Programm durchgeführten Funktionsaufrufe zusammen mit Statistiken zu jedem:
- Oben (erste Zeile in Blau) sehen wir die Gesamtzahl der im Profilprogramm getätigten Aufrufe und die Gesamtausführungszeit. Möglicherweise sehen Sie auch eine Zahl für "primitive Aufrufe", dh nicht rekursive Aufrufe, oder Aufrufe direkt an eine Funktion, die sich nicht weiter unten im Aufrufstapel aufruft.
- ncalls : Anzahl der getätigten Anrufe. Wenn Sie zwei durch einen Schrägstrich getrennte Zahlen sehen, ist die zweite Zahl die Anzahl der primitiven Aufrufe für diese Funktion.
- tottime : Gesamtzeit, die in der Funktion verbracht wurde, ohne Aufrufe anderer Funktionen.
- Percall : Durchschnittliche Zeit pro Anruf für Tottime , abgeleitet aus Tottime und Division durch ncalls .
- cumtime : Gesamtzeit, die in der Funktion verbracht wurde, einschließlich Aufrufen anderer Funktionen.
- percall (# 2): Durchschnittliche Zeit pro Anruf für cumtime ( cumtime geteilt durch ncalls ).
- Dateiname: lineno : Der Dateiname, die Zeilennummer und der Funktionsname für den betreffenden Anruf.
So ändern Sie cProfile-Berichte
Standardmäßig wird cProfile
die Ausgabe nach "Standardname" sortiert, dh nach dem Text in der rechten Spalte (Dateiname, Zeilennummer usw.).
Das Standardformat ist nützlich, wenn Sie einen allgemeinen Top-Down-Bericht über jeden einzelnen Funktionsaufruf als Referenz wünschen. Wenn Sie jedoch versuchen, einem Engpass auf den Grund zu gehen, möchten Sie wahrscheinlich die zeitaufwändigsten Teile des Programms zuerst auflisten.
Wir können diese Ergebnisse erzielen, indem wir cProfile
etwas anders aufrufen . Beachten Sie, wie der untere Teil des obigen Programms überarbeitet werden kann, um die Statistiken nach einer anderen Spalte zu sortieren (in diesem Fall ncalls
):
if __name__ == '__main__': cProfile importieren, pstats profiler = cProfile.Profile () profiler.enable () main () profiler.disable () stats = pstats.Stats (profiler) .sort_stats ('ncalls') stats.print_stats ()
Die Ergebnisse sehen ungefähr so aus:

So funktioniert das alles:
- Anstatt einen Befehl haft auszuführen
cProfile.run()
, die nicht sehr flexibel ist, schaffen wir ein Profilierungs Objekt ,profiler
. - Wenn wir eine Aktion profilieren möchten, rufen wir zuerst
.enable()
die Profiler-Objektinstanz auf, führen dann die Aktion aus und rufen dann auf.disable()
. (Dies ist eine Möglichkeit, nur einen Teil eines Programms zu profilieren.) - Das
pstats
Modul wird verwendet, um die vom Profiler-Objekt gesammelten Ergebnisse zu bearbeiten und diese Ergebnisse zu drucken.
Durch das Kombinieren eines Profiler-Objekts pstats
können wir die erfassten Profildaten bearbeiten, um beispielsweise die generierten Statistiken anders zu sortieren. In diesem Beispiel werden .sort_stats('ncalls')
die Statistiken mithilfe von nach ncalls
Spalte sortiert . Andere Sortieroptionen sind verfügbar.
Verwendung von cProfile-Ergebnissen zur Optimierung
Die für die cProfile
Ausgabe verfügbaren Sortieroptionen ermöglichen es uns, potenzielle Leistungsengpässe in einem Programm herauszufiltern.
ncalls
Die erste und wichtigste Information, mit der Sie aufdecken können, cProfile
ist, welche Funktionen über die ncalls
Spalte am häufigsten aufgerufen werden .
In Python verursacht das bloße Ausführen eines Funktionsaufrufs einen relativ hohen Overhead. Wenn eine Funktion wiederholt in einer engen Schleife aufgerufen wird, auch wenn es sich nicht um eine Funktion mit langer Laufzeit handelt, wirkt sich dies garantiert auf die Leistung aus.
Im obigen Beispiel wird die Funktion add
(und die Funktion add_2
) wiederholt in einer Schleife aufgerufen. Das Verschieben der Schleife in die add
Funktion selbst oder das add
vollständige Inlinen der Funktion würde dieses Problem beheben.
tottime
Weitere nützliche statistische Details, welche Funktionen das Programm über die tottime
Spalte am meisten ausführt .
Im obigen Beispiel verwendet die add_2
Funktion eine Schleife, um eine teure Berechnung zu simulieren, die ihre tottime
Punktzahl nach oben schiebt . Jede Funktion mit einer hohen tottime
Punktzahl verdient einen genauen Blick, insbesondere wenn sie mehrmals oder in einer engen Schleife aufgerufen wird.
Beachten Sie, dass Sie immer den Kontext berücksichtigen müssen, in dem die Funktion verwendet wird. Wenn eine Funktion ein High hat, tottime
aber nur einmal aufgerufen wird - zum Beispiel nur beim Starten des Programms -, ist es weniger wahrscheinlich, dass es sich um einen Engpass handelt. Wenn Sie jedoch versuchen, die Startzeit zu verkürzen, möchten Sie wissen, ob eine beim Start aufgerufene Funktion alles andere warten lässt.
So exportieren Sie cProfile-Daten
Wenn Sie die cProfile
generierten Statistiken auf erweiterte Weise verwenden möchten , können Sie sie in eine Datendatei exportieren:
stats = pstats.Stats (Profiler) stats.dump_stats ('/ path / to / stats_file.dat')
Diese Datei kann mithilfe des pstats
Moduls zurückgelesen und dann sortiert oder mit angezeigt werden pstats
. Die Daten können auch von anderen Programmen wiederverwendet werden. Zwei Beispiele:
pyprof2calltree
Rendert detaillierte Visualisierungen des Aufrufdiagramms und der Nutzungsstatistiken des Programms aus Profildaten. Dieser Artikel enthält ein detailliertes Beispiel aus der Praxis.snakeviz
generiert auch Visualisierungen auscProfile
Daten, verwendet jedoch eine andere Darstellung für die Daten - einen „Sunburst“ anstelle des „Flammen“ -Diagramms von pyprof2calltree.
Über cProfile für Python-Profiling hinaus
cProfile
ist kaum die einzige Möglichkeit, eine Python-Anwendung zu profilieren. cProfile
ist sicherlich eine der bequemsten Möglichkeiten, da es mit Python gebündelt ist. Aber andere verdienen Aufmerksamkeit.
Ein Projekt erstellt py-spy
ein Profil für eine Python-Anwendung, indem es deren Aufrufaktivität abtastet. py-spy
kann verwendet werden, um eine laufende Python-App zu untersuchen, ohne sie stoppen und neu starten zu müssen und ohne ihre Codebasis ändern zu müssen, sodass sie zum Profilieren bereitgestellter Anwendungen verwendet werden kann. py-spy
generiert auch einige Statistiken über den Overhead, der durch die Python-Laufzeit entsteht (z. B. Garbage Collection-Overhead), was cProfile
nicht der Fall ist.