Python mit Cython beschleunigen

Cython ist eine Obermenge von Python, mit der Sie die Geschwindigkeit Ihres Codes erheblich verbessern können. Sie können optionale Typdeklarationen hinzufügen, um noch größere Vorteile zu erzielen. Cython übersetzt Ihren Code in optimiertes C / C ++, das zu einem Python-Erweiterungsmodul kompiliert wird. 

In diesem Lernprogramm erfahren Sie, wie Sie Cython installieren, sofort eine kostenlose Leistungssteigerung Ihres Python-Codes erhalten und anschließend Cython wirklich nutzen können, indem Sie Typen hinzufügen und Ihren Code profilieren. Schließlich erfahren Sie mehr über fortgeschrittenere Themen wie die Integration mit C / C ++ - Code und NumPy, die Sie für noch mehr Nutzen ausloten können.

Pythagoräische Tripel zählen

Pythagoras war ein griechischer Mathematiker und Philosoph. Er ist berühmt für sein pythagoreisches Theorem, das besagt, dass in einem rechtwinkligen Dreieck die Summe der Quadrate der Schenkel der Dreiecke dem Quadrat der Hypotenuse entspricht. Pythagoras-Tripel sind beliebige drei positive ganze Zahlen a, b und c, die solche a² + b² = c². Hier ist ein Programm, das alle pythagoreischen Tripel findet, deren Mitglieder die angegebene Grenze nicht überschreiten.

Importzeit def count (Limit): Ergebnis = 0 für a im Bereich (1, Grenzwert + 1): für b im Bereich (a + 1, Grenzwert + 1): für c im Bereich (b + 1, Grenzwert + 1) : wenn c * c> a * a + b * b: break wenn c * c == (a * a + b * b): Ergebnis + = 1 Ergebnis zurückgeben, wenn __name__ == '__main__': start = time.time () Ergebnis = Zählung (1000) Dauer = Zeit.Zeit () - Druck starten (Ergebnis, Dauer) Ausgabe: 881 13.883624076843262 

Anscheinend gibt es 881 Tripel, und das Programm brauchte etwas weniger als 14 Sekunden, um es herauszufinden. Das ist nicht zu lang, aber lang genug, um nervig zu sein. Wenn wir mehr Tripel bis zu einer höheren Grenze finden wollen, sollten wir einen Weg finden, um es schneller zu machen. 

Es stellt sich heraus, dass es wesentlich bessere Algorithmen gibt, aber heute konzentrieren wir uns darauf, Python mit Cython schneller zu machen, und nicht auf den besten Algorithmus, um pythagoreische Tripel zu finden. 

Einfaches Boosting mit Pyximport

Die einfachste Möglichkeit, Cython zu verwenden, ist die spezielle Pyximport-Funktion. Dies ist eine Anweisung, die Ihren Cython-Code im Handumdrehen kompiliert und Ihnen die Vorteile der systemeigenen Optimierung ohne großen Aufwand ermöglicht. 

Sie müssen den Code für die Cythonisierung in ein eigenes Modul einfügen, eine Zeile Setup in Ihr Hauptprogramm schreiben und dann wie gewohnt importieren. Mal sehen, wie es aussieht. Ich habe die Funktion in eine eigene Datei mit dem Namen pythagorean_triples.pyx verschoben. Die Erweiterung ist wichtig für Cython. Die Zeile, die Cython aktiviert, ist pyximport importieren; pyximport.install (). Dann importiert es einfach das Modul mit Anzahl() Funktion und ruft sie später in der Hauptfunktion auf.

import zeit import pyximport; pyximport.install () import pythagorean_triples def main (): start = time.time () result = pythagorean_triples.count (1000) duration = time.time () - Druck starten (Ergebnis, duration), wenn __name__ == '__main__': main () Ausgang: 881 9.432806253433228 

Die reine Python-Funktion lief um 50% länger. Wir haben diesen Schub durch das Hinzufügen einer einzelnen Zeile erhalten. Gar nicht so schlecht.

Bauen Sie Ihr eigenes Erweiterungsmodul auf

Während pyximport während der Entwicklung sehr praktisch ist, funktioniert es nur mit reinen Python-Modulen. Bei der Optimierung von Code möchten Sie häufig auf native C-Bibliotheken oder Python-Erweiterungsmodule verweisen. 

Um dies zu unterstützen und das dynamische Kompilieren bei jedem Lauf zu vermeiden, können Sie ein eigenes Cython-Erweiterungsmodul erstellen. Sie müssen eine kleine setup.py-Datei hinzufügen und daran denken, sie vor dem Ausführen des Programms zu erstellen, wenn Sie den Cython-Code ändern. Hier ist die setup.py-Datei:

von distutils.core importieren Sie Setup von Cython.Build import cythonize setup (ext_modules = cythonize ("pythagorean_triples.pyx"))

Dann musst du es bauen:

$ python setup.py build_ext --inplace Kompiliert pythagorean_triples.pyx, da sich dies geändert hat. [1/1] Cythonizing pythagorean_triples.pyx ausgeführt build_ext building Erweiterung 'pythagorean_triples' erstellen build erstellen build / temp.macosx-10.7-x86_64-3.6 gcc -Wno-unused-result -Wignign-Compare -unreachable-code -DNDEBUG -g - fwrapv -O3 -Wall -Wurz-Prototypen -I / Benutzer / gigi.sayfan / miniconda3 / envs / py3 / include -arch x86_64 -I / Benutzer / gigi.sayfan / miniconda3 / envs / py3 / include -arch x86_64 -I / Benutzer / gigi.sayfan / miniconda3 / envs / py3 / include / python3.6m -c pythagorean_triples.c -o build / temp.macosx-10.7-x86_64-3.6 / pythagorean_triples.o gcc -bundle -undefined dynamic_lookup -L / Users / gigi.sayfan / miniconda3 / envs / py3 / lib -L / Benutzer / gigi.sayfan / miniconda3 / envs / py3 / lib -arch x86_64 build / temp.macosx-10.7-x86_64-3.6 / pythagorean_triples.o -L / Users / gigi.sayfan / miniconda3 / envs / py3 / lib -o pythagorean_triples.cpython-36m-darwin.so

Wie Sie anhand der Ausgabe sehen können, hat Cython eine C-Datei mit dem Namen pythagorean_triples.c generiert und eine plattformspezifische .so-Datei kompiliert. Hierbei handelt es sich um das Erweiterungsmodul, das Python jetzt wie jedes andere native Erweiterungsmodul importieren kann. 

Wenn Sie neugierig sind, werfen Sie einen Blick auf den generierten C-Code. Es ist sehr lang (2789 Zeilen), unübersichtlich und enthält eine Menge zusätzliches Material, um mit der Python-API zu arbeiten. Lassen Sie uns den Pyximport fallen und führen Sie unser Programm erneut aus:

import time import pythagorean_triples def main (): start = time.time () Ergebnis = pythagorean_triples.count (1000) duration = time.time () - Druck starten (Ergebnis, Duration) wenn __name__ == '__main__': main () 881 9.507064819335938 

Das Ergebnis ist ziemlich dasselbe wie bei pyximport. Beachten Sie jedoch, dass ich nur die Laufzeit des cythonisierten Codes messe. Ich messe nicht, wie lange pyximport benötigt, um den cythonisierten Code im laufenden Betrieb zu kompilieren. In großen Programmen kann dies erheblich sein.

Typen zu Ihrem Code hinzufügen

Lassen Sie uns es auf die nächste Stufe bringen. Cython ist mehr als Python und fügt optionales Tippen hinzu. Hier definiere ich einfach alle Variablen als Ganzzahlen und die Leistungssteigerungen:

# pythagorean_triples.pyx def count (limit): cdef int ergebnis = 0 cdef int a = 0 cdef int b = 0 cdef int c = 0 für a im bereich (1, limit + 1): für b im bereich (a + 1 Grenzwert + 1): für c im Bereich (b + 1, Grenzwert + 1): Wenn c * c> a * a + b * b: break, wenn c * c == (a * a + b * b): Ergebnis + = 1 Ergebnis zurückgeben ---------- # main.py import time import pyximport; pyximport.install () import pythagorean_triples def main (): start = time.time () result = pythagorean_triples.count (1000) duration = time.time () - Druck starten (Ergebnis, duration), wenn __name__ == '__main__': Haupt () - Ausgang: 881 0,056414127349853516 

Ja. Das ist richtig. Durch die Definition einiger Ganzzahlen läuft das Programm in weniger als 57 Millisekunden, im Vergleich zu mehr als 13 Sekunden bei reinem Python. Das ist fast eine Verbesserung um das 250-fache.

Profilieren Sie Ihren Code

Ich habe Pythons Zeitmodul verwendet, das die Wandzeit misst und die meiste Zeit ziemlich gut ist. Wenn Sie ein genaueres Timing für kleine Codefragmente wünschen, sollten Sie das Timeit-Modul verwenden. So messen Sie die Leistung des Codes anhand von Timeit:

>>> Zeit importieren >>> timeit.timeit ('count (1000)', setup = "from pythagorean_triples import count", number = 1) 0.05357028398429975 # Läuft 10 mal >>> timeit.timeit ('count (1000)') , setup = "from pythagorean_triples import count", number = 10) 0.5446877249924 

Das Zeit () Die Funktion benötigt eine Anweisung, einen Setup-Code, der nicht gemessen wird, und die Häufigkeit, mit der der gemessene Code ausgeführt wird.

Fortgeschrittene Themen

Ich habe gerade die Oberfläche hier gekratzt. Mit Cython können Sie noch viel mehr tun. Im Folgenden finden Sie einige Themen, die die Leistung Ihres Codes weiter verbessern oder die Integration von Cython in andere Umgebungen ermöglichen:

  • C-Code aufrufen
  • Interaktion mit der Python C-API und der GIL
  • mit C ++ in Python
  • Cython-Code zu PyPY portieren
  • unter Verwendung der Parallelität
  • Cython und NumPy
  • Freigeben von Deklarationen zwischen Cython-Modulen

Fazit

Cython kann bei sehr geringem Aufwand zwei Größenordnungen der Leistungssteigerung erzielen. Wenn Sie in Python nicht triviale Software entwickeln, ist Cython ein Kinderspiel. Es hat sehr wenig Aufwand, und Sie können es schrittweise in Ihre Codebase einfügen.

Zögern Sie nicht, zu sehen, was wir auf dem Markt zum Verkauf und zum Lernen zur Verfügung haben, und zögern Sie nicht, Fragen zu stellen und mit dem untenstehenden Feed wertvolles Feedback zu geben.