Web

Python Code

Die besten Profiler

09.08.2021
Von 
Serdar Yegulalp schreibt für unsere US-Schwesterpublikation Infoworld.
Python Profiler helfen Ihnen dabei, herauszufinden, wo es bei der Performance Ihrer App hakt.
Code Profiler geben Aufschluss darüber, an welchen Stellen Ihre Python App unzureichend performt.
Code Profiler geben Aufschluss darüber, an welchen Stellen Ihre Python App unzureichend performt.
Foto: slowmotiongli - shutterstock.com

Jede Programmiersprache weist zwei Arten von Geschwindigkeit auf: Entwicklungs- und Ausführungsgeschwindigkeit. Im Fall von Python lag der Fokus schon immer darauf, schnell zu schreiben. Obwohl Python-Code fast immer schnell genug für die jeweilige Aufgabe ist - ist er es nicht, müssen Sie herausfinden, an welchen Stellen und etwas dagegen tun. Einfach nur zu vermuten, was falsch läuft, ist auch bei Software keine gute Idee. Stattdessen bietet es sich an, Statistiken und Metriken einzuholen, um Aufschluss über die tatsächliche Programmleistung zu gewinnen.

Die gute Nachricht: Python bietet eine Reihe von Paketen, mit denen Sie ein Profil Ihrer Anwendungen erstellen können, um Performance-Schwächen auf die Spur zu kommen. Diese Tools reichen von simplen Einzeilern bis hin zu ausgefeilten Frameworks, die Statistiken über laufende Anwendungen erfassen.

Wir stellen Ihnen die unserer Meinung nach wichtigsten Python Profiler vor. Sie alle laufen plattformübergreifend und sind entweder in PyPI oder in der Python-Standardbibliothek verfügbar.

Time und Timeit

Wenn Sie nur die Zeit zwischen zwei Code-Schnipseln messen wollen, deren Ausführung Sekunden oder Minuten dauert, dann ist eine Stoppuhr mehr als ausreichend.

Die Python-Standardbibliothek verfügt über zwei Funktionen, die als Stoppuhren fungieren. Das Time-Modul perf_counter ruft den High-Resolution Timer des Betriebssystems auf, um einen beliebigen Zeitstempel zu erzeugen. Rufen Sie time.perf_counter einmal vor und einmal nach einer Aktion auf, um die Differenz zwischen den beiden Werten zu erhalten. Eine gute Lösung, wenn Sie schnell und einfach Zeitmessungen durchführen wollen.

Das Timeit-Modul versucht sich an einer Art Benchmarking für Python-Code. Die Funktion timeit.timeit führt einen Code-Schnipsel viele Male aus (die Vorgabe lautet eine Million Durchläufe) und ermittelt die dafür benötigte Gesamtzeit. Es eignet sich am besten dafür, festzustellen, wie eine einzelne Operation oder ein Funktionsaufruf in einer engen Schleife performt.

Der Nachteil von Time ist, dass es nichts weiter als eine Stoppuhr darstellt. Der Nachteil von Timeit ist hingegen, dass sein Hauptanwendungsfall Mikro-Benchmarks für einzelne Code-Zeilen oder -Blöcke sind. Diese Module funktionieren nur, wenn Sie mit isoliertem Code arbeiten - keines reicht für die Analyse des gesamten Programms aus.

cProfile

Die Python-Standardbibliothek enthält ebenfalls einen vollständigen Profiler. Wird cProfile ausgeführt, verfolgt es jeden Funktionsaufruf in Ihrem Programm und erstellt eine Liste, welche Funktionen am häufigsten verwendet wurden und wie lange die Aufrufe im Durchschnitt gedauert haben.

cProfile weist drei große Stärken auf:

  1. ist es in der Standardbibliothek enthalten, so dass es sogar in einer Standard-Python-Installation verfügbar ist.

  2. erstellt es eine Reihe verschiedener Statistiken über das Aufrufverhalten. Auf diese Weise können Sie feststellen, ob eine Funktion selbst langsam ist oder ob sie andere Funktionen initiiert, die langsam sind.

  3. können Sie cProfile frei einschränken. Sie können eine Stichprobe des gesamten Programmlaufs nehmen oder die Profilerstellung nur bei der Ausführung einer bestimmten Funktion aktivieren. Dieser Ansatz funktioniert am besten, wenn Sie die Ursache bereits eingegrenzt haben - erspart es Ihnen aber, sich durch eine vollständige Profilaufzeichnung zu wühlen.

Das bringt uns zu einem Nachteil von cProfile: Es generiert standardmäßig eine Menge Statistiken. Der Versuch, die Nadel in all den Heuhaufen zu finden, kann überwältigen. Ein anderer Nachteil ist das Ausführungsmodell von cProfile: Es fängt jeden einzelnen Funktionsaufruf ab, was einen erheblichen Overhead verursacht. cProfile ist deshalb eher ungeeignet für das Profiling von Anwendungen in der Produktion mit Live-Daten, ist aber gut geeignet, um während der Entwicklung eingesetzt zu werden.

Palanteer

Palanteer ist eine relativ neue Ergänzung zum Python-Profiling-Arsenal und kann sowohl für Python- als auch für C++-Programme verwendet werden. Besonders nützlich ist Palanteer, wenn Sie eine Python-Anwendung schreiben, die selbst erstellte C++-Bibliotheken beinhaltet und Sie einen möglichst detaillierten Einblick in beide Komponenten haben möchten. Palanteer bereitet die Ergebnisse in einer grafischen Benutzeroberfläche auf, die live aktualisiert wird, während Ihr Programm läuft.

Die Instrumentierung von Python-Anwendungen ist denkbar einfach. Funktionsaufrufe, Ausnahmen, Garbage Collection und Speicherzuweisungen auf OS-Ebene werden aufgezeichnet. Letztgenannte Funktionen sind besonders nützlich, wenn sich herausstellt, dass die Leistungsprobleme Ihrer Anwendung mit der Speichernutzung oder den Objektzuweisungen zusammenhängen.

Ein großer Nachteil von Palanteer - zumindest im Moment: Sie müssen es komplett aus dem Quellcode erstellen, es sind noch keine vorkompilierten Binärdateien verfügbar. Sie müssen also Ihren alten C++-Compiler hervorkramen - und auch eine Kopie des CPython-Quellcodes ist zu empfehlen.

Pyinstrument

Pyinstrument funktioniert wie cProfile, hat jedoch zwei große Vorteile gegenüber dem Profiler der Standardbibliothek:

  1. Pyinstrument versucht nicht, jede einzelne Instanz eines Funktionsaufrufs abzufangen. Es tastet den Aufrufstapel des Programms jede Millisekunde ab. So ist es weniger aufdringlich, kann aber immer noch gut erkennen, was den größten Teil der Laufzeit Ihres Programms verschlingt.

  2. sind die Berichte von Pyinstrument viel übersichtlicher. Es zeigt Ihnen die wichtigsten Funktionen in Ihrem Programm in schnell und leicht konsumierbaren Ergebnissen.

Pyinstrument bietet auch viele der Annehmlichkeiten von cProfile: Sie können den Profiler als Objekt in Ihrer Anwendung verwenden und nur das Verhalten ausgewählter Funktionen aufzeichnen. Der Output kann auf verschiedene Art und Weise gerendert werden, auch in HTML. Wenn Sie die gesamte Zeitachse der Aufrufe sehen möchten, ist das ebenfalls kein Problem.

Doch auch Pyinstrument hat Nachteile: Einige Programme, die in C kompilierte Erweiterungen verwenden (etwa solche die mit Cython erstellt wurden), funktionieren möglicherweise nicht richtig, wenn sie über die Befehlszeile aufgerufen werden. Dann muss Pyinstrument im Programm selbst verwendet werden - zum Beispiel, indem Sie eine main()-Funktion mit einem Pyinstrument-Profiler-Aufruf verknüpfen. Eine weitere Einschränkung: Pyinstrument kann nicht gut mit Code umgehen, der in mehreren Threads läuft.

Py-spy

Py-spy arbeitet wie Pyinstrument und tastet den Call Stack eines Programms in regelmäßigen Abständen ab, statt zu versuchen, jeden einzelnen Aufruf aufzuzeichnen. Anders als PyInstrument weist Py-spy Kernkomponenten auf, die in Rust geschrieben sind. Der Profiler wird isoliert vom profilierten Programm ausgeführt, so dass er verwendet werden kann, während der Code im Produktivbetrieb läuft.

Diese Architektur verleiht Py-spy Fähigkeiten, die viele andere Profiler nicht haben - beispielsweise Multithreading und Subprocessing von Python-Anwendungen. Py-spy kann auch C-Erweiterungen profilieren, aber diese müssen dafür mit Symbolen kompiliert werden. Im Fall von Erweiterungen, die mit Cython kompiliert wurden, muss die generierte C-Datei vorhanden sein, um die richtigen Trace-Informationen zu sammeln.

Es gibt zwei grundlegende Möglichkeiten, eine Anwendung mit Py-spy zu untersuchen:

  1. Sie können die Anwendung mit dem record-Befehl von Py-spy ausführen, der nach Abschluss ein Flammendiagramm erzeugt.

  2. Oder Sie können die Anwendung mit dem top-Befehl ausführen, der eine live-aktualisierte, interaktive Anzeige des Innenlebens Ihrer Python-Anwendung erzeugt. Einzelne Thread-Stacks können auch von der Kommandozeile aus ausgegeben werden.

Py-spy hat einen großen Nachteil: Es ist hauptsächlich dazu gedacht, ein ganzes Programm oder einige seiner Komponenten von außen zu profilieren. Nur bestimmte Funktionen zu testen ist nicht möglich.

Yappi

Yappi ("Yet Another Python Profiler") bringt einige Alleinstellungsmerkmale mit, die kein anderer Python Profiler zu bieten hat. PyCharm installiert Yappi standardmäßig als Profiler der Wahl, so dass Benutzer dieser IDE bereits Zugriff auf Yappi haben.

Um Yappi zu verwenden, statten Sie Ihren Code mit Anweisungen aus, um Berichte für Profiling-Mechanismen aufzurufen, zu starten und zu stoppen. Yappi lässt Sie zwischen "Wall Time" und "CPU Time" wählen, um die benötigte Zeit zu messen. Erstere ist lediglich eine Stoppuhr; letztere misst über systemeigene APIs, wie lange die CPU tatsächlich mit der Ausführung von Code beschäftigt war, wobei Pausen für E/A oder schlafende Threads ausgelassen werden. Die CPU-Zeit vermittelt Ihnen ein gutes Gefühl dafür, wie lange bestimmte Operationen tatsächlich dauern.

Mit Hilfe der Funktion yappi.get_thread_stats() ruft Yappi die Statistiken jeder aufgezeichneten Thread-Aktivität ab, die Sie dann separat auswerten können. Die Statistiken können mit hoher Granularität gefiltert und sortiert werden, ähnlich wie mit cProfile. Darüber hinaus ist Yappi auch in der Lage, Greenlets und Coroutines zu profilieren, was viele andere Profiler nicht können. (fm)

Dieser Beitrag basiert auf einem Artikel unserer US-Schwesterpublikation Infoworld.