Python hat sich den Ruf erworben, leistungsfähig, flexibel und einfach in der Anwendung zu sein. Diese Vorzüge haben dazu geführt, dass sie in einer großen und wachsenden Vielfalt von Anwendungen, Arbeitsabläufen und Bereichen eingesetzt wird. Aber das Design der Sprache – ihre interpretierte Natur, ihre Laufzeitdynamik – bedeutet, dass Python schon immer um eine Größenordnung langsamer war als maschinennative Sprachen wie C oder C++.
Im Laufe der Jahre haben Entwickler eine Reihe von Umgehungen für die Geschwindigkeitsbegrenzungen von Python entwickelt. So kann man beispielsweise leistungsintensive Aufgaben in C schreiben und sie mit Python ummanteln; viele Bibliotheken für maschinelles Lernen tun genau das. Oder Sie könnten Cython verwenden, ein Projekt, mit dem Sie Python-Code mit Laufzeittypinformationen versehen können, die es ermöglichen, ihn in C zu kompilieren.
Aber Umgehungslösungen sind nie ideal. Wäre es nicht großartig, wenn wir ein bestehendes Python-Programm einfach so nehmen könnten, wie es ist, und es dramatisch schneller ausführen könnten? Das ist genau das, was PyPy ermöglicht.
PyPy vs. CPython
PyPy ist ein Ersatz für den Standard-Python-Interpreter, CPython. Während CPython Python in Byte-Zwischencode kompiliert, der dann von einer virtuellen Maschine interpretiert wird, verwendet PyPy die Just-in-Time-Kompilierung (JIT), um Python-Code in maschinennative Assemblersprache zu übersetzen.
Abhängig von der durchzuführenden Aufgabe kann der Leistungsgewinn dramatisch sein. Im Durchschnitt beschleunigt PyPy Python um das 7,6-fache, manche Aufgaben sogar um das 50-fache oder mehr. Der CPython-Interpreter führt einfach nicht die gleichen Optimierungen durch wie PyPy und wird dies wahrscheinlich auch nie tun, da dies nicht zu seinen Entwicklungszielen gehört.
Das Beste daran ist, dass der Entwickler wenig bis gar keinen Aufwand betreiben muss, um die Vorteile von PyPy zu nutzen. Tauschen Sie einfach CPython gegen PyPy aus, und schon sind Sie größtenteils fertig. Es gibt ein paar Ausnahmen, die weiter unten besprochen werden, aber das erklärte Ziel von PyPy ist es, bestehenden, unmodifizierten Python-Code auszuführen und ihn mit einem automatischen Geschwindigkeitsschub zu versehen.
PyPy unterstützt derzeit sowohl Python 2 als auch Python 3, und zwar in Form verschiedener Versionen des Projekts. Mit anderen Worten, Sie müssen verschiedene Versionen von PyPy herunterladen, je nachdem, welche Version von Python Sie verwenden möchten. Den Python 2-Zweig von PyPy gibt es schon viel länger, aber die Python 3-Version wurde in letzter Zeit auf den neuesten Stand gebracht. Sie unterstützt derzeit sowohl Python 3.5 (Produktionsqualität) als auch Python 3.6 (Beta-Qualität).
Zusätzlich zur Unterstützung der gesamten Python-Kernsprache arbeitet PyPy mit der großen Mehrheit der Werkzeuge im Python-Ökosystem, wie pip
für die Paketierung oder virtualenv
für virtuelle Umgebungen. Die meisten Python-Pakete, sogar die mit C-Modulen, sollten so funktionieren, wie sie sind, obwohl es Einschränkungen gibt, auf die wir weiter unten eingehen werden.
Wie PyPy funktioniert
PyPy verwendet Optimierungstechniken, die man in anderen Just-in-Time-Compilern für dynamische Sprachen findet. Es analysiert laufende Python-Programme, um die Typinformationen von Objekten zu ermitteln, während sie erstellt und in Programmen verwendet werden, und verwendet diese Typinformationen dann als Leitfaden, um die Dinge zu beschleunigen. Wenn zum Beispiel eine Python-Funktion nur mit einem oder zwei verschiedenen Objekttypen arbeitet, generiert PyPy Maschinencode, um diese speziellen Fälle zu behandeln.
Die Optimierungen von PyPy werden automatisch zur Laufzeit durchgeführt, so dass Sie im Allgemeinen die Leistung nicht optimieren müssen. Ein fortgeschrittener Benutzer könnte mit den Befehlszeilenoptionen von PyPy experimentieren, um schnelleren Code für spezielle Fälle zu erzeugen, aber das ist nur selten notwendig.
PyPy weicht auch von der Art und Weise ab, wie CPython einige interne Funktionen behandelt, versucht aber, kompatible Verhaltensweisen beizubehalten. Zum Beispiel behandelt PyPy die Garbage Collection anders als CPython. Nicht alle Objekte werden sofort eingesammelt, wenn sie den Gültigkeitsbereich verlassen. Daher kann ein Python-Programm, das unter PyPy läuft, einen größeren Speicherbedarf aufweisen als ein Programm, das unter CPython läuft. Aber Sie können immer noch Pythons High-Level-Garbage-Collection-Kontrollen verwenden, die durch das gc
-Modul zugänglich gemacht werden, wie z.B. gc.enable()
, gc.disable()
und gc.collect()
.
Wenn Sie Informationen über das JIT-Verhalten von PyPy zur Laufzeit wünschen, enthält PyPy ein Modul, pypyjit
, das viele JIT-Hooks für Ihre Python-Anwendung bereitstellt. Wenn Sie eine Funktion oder ein Modul haben, das schlecht mit dem JIT funktioniert, können Sie mit pypyjit
detaillierte Statistiken darüber erhalten.
Ein weiteres PyPy-spezifisches Modul, __pypy__
, stellt andere PyPy-spezifische Funktionen zur Verfügung und kann daher nützlich sein, um Anwendungen zu schreiben, die diese Funktionen nutzen. Aufgrund der Laufzeitdynamik von Python ist es möglich, Python-Anwendungen zu erstellen, die diese Funktionen nutzen, wenn PyPy vorhanden ist, und sie ignorieren, wenn es nicht vorhanden ist.
PyPy-Einschränkungen
So magisch PyPy auch erscheinen, es ist keine Magie. PyPy hat bestimmte Einschränkungen, die seine Effektivität für bestimmte Arten von Programmen einschränken oder ausschließen. Leider ist PyPy kein universeller Ersatz für die CPython-Laufzeitumgebung.
PyPy funktioniert am besten mit reinen Python-Anwendungen
PyPy hat immer am besten mit „reinen“ Python-Anwendungen funktioniert – d.h. Anwendungen, die in Python und nichts anderem geschrieben sind. Python-Pakete mit Schnittstellen zu C-Bibliotheken, wie NumPy, haben nicht so gut abgeschnitten, weil PyPy die nativen Binärschnittstellen von CPython emuliert.
Die Entwickler von PyPy haben an diesem Problem gefeilt und PyPy kompatibler mit den meisten Python-Paketen gemacht, die von C-Erweiterungen abhängen. Numpy, zum Beispiel, funktioniert jetzt sehr gut mit PyPy. Wenn Sie jedoch maximale Kompatibilität mit C-Erweiterungen wünschen, sollten Sie CPython verwenden.
PyPy funktioniert am besten mit länger laufenden Programmen
Einer der Nebeneffekte der PyPy-Optimierung von Python-Programmen ist, dass länger laufende Programme am meisten von seinen Optimierungen profitieren. Je länger das Programm läuft, desto mehr Typinformationen kann PyPy zur Laufzeit sammeln und desto mehr Optimierungen kann es vornehmen. Einmalige Python-Skripte werden davon nicht profitieren. Die Anwendungen, die davon profitieren, haben typischerweise Schleifen, die über lange Zeiträume laufen, oder laufen kontinuierlich im Hintergrund – Web-Frameworks zum Beispiel.
PyPy kompiliert nicht im Voraus
PyPy kompiliert Python-Code, aber es ist kein Compiler für Python-Code. Aufgrund der Art und Weise, wie PyPy seine Optimierungen durchführt, und der inhärenten Dynamik von Python gibt es keine Möglichkeit, den resultierenden JIT-kompilierten Code als eigenständige Binärdatei auszugeben und wiederzuverwenden. Jedes Programm muss für jeden Lauf neu kompiliert werden. Wenn Sie Python in schnelleren Code kompilieren wollen, der als eigenständige Anwendung laufen kann, verwenden Sie Cython, Numba oder das derzeit experimentelle Nuitka-Projekt.