12 Punkte von darjeeling 2025-05-23 | 3 Kommentare | Auf WhatsApp teilen

Beim jüngsten Lernen zu free-threading Python bin ich auf PyO3 aufmerksam geworden, daher teile ich diesen zwei Jahre alten Artikel.

Making Python 100× Faster with <100 Lines of Rust – Zusammenfassung

Hintergrund

  • Die zentrale Python-Bibliothek einer internen 3-D-Verarbeitungspipeline wurde durch die steigende Zahl gleichzeitiger Nutzer zum Flaschenhals.
  • Da ein komplettes Neuschreiben in Rust zu riskant und zeitaufwendig gewesen wäre, fiel die Wahl auf eine partielle Optimierung.

Vorgehensweise

  1. Zuerst messen: Mit dem Sampling-Profiler py-spy wurde der Flaschenhals identifiziert.
  2. Schrittweise Einführung von Rust
    • Verbindung zwischen Python und Rust mit PyO3 + maturin.
    • Zunächst wurde nur die Funktion find_close_polygons nach Rust portiert.
    • Anschließend wurde auch die Datenstruktur Polygon nach Rust verschoben und in Python per Subclassing genutzt.
  3. Wiederholtes Profiling und Verbessern
    • Unnötige NumPy → Rust-Konvertierungen minimiert.
    • Zuweisungen und Kopien reduziert sowie durch direkte Distanzberechnung feinoptimiert.

Leistungsentwicklung

Phase Durchschnittliche Laufzeit (ms) Verbesserungsfaktor
Ursprünglich reines Python 293.41
v1 – nur Funktion in Rust (--release) 23.44 12.5×
v2 – auch Polygon in Rust 6.29 46.5×
v3 – Zuweisungen entfernt, direkte Berechnung 2.90 101×

Zentrale Technologien

  • PyO3 : sicheres Python ↔ Rust-FFI.
  • maturin : Automatisierung von Build und Deployment.
  • ndarray / numpy crate : Arrays und lineare Algebra auf Rust-Seite.
  • py-spy : Profiler mit Einblick bis in den nativen Stack.

Erkenntnisse

  • Wer zuerst profiliert, kann mit kleinen Codeänderungen große Gewinne erzielen.
  • Selbst wenn nur das Rust-Modul ausgetauscht wird und die Python-API erhalten bleibt, lässt sich das sofort in produktiven Services einsetzen.
  • Rust ist auch dann sehr effektiv, wenn es nur dünn für den „Performance-Bereich“ eingeführt wird.

3 Kommentare

 
allinux 2025-05-23

Python-Erweiterungen in C/C++ zu erstellen, ist produktivitätsmäßig viel zu ineffizient, aber PyO3 ist dank maturin und cargo vor allem sehr komfortabel.
Außerdem ist Cross-Compilation für Python-Module unverzichtbar, und auch dabei ist Rust praktisch.

 
lamanus 2025-05-23

maturin... Qual...

 
aer0700 2025-05-23

Ich halte mich so lange wie möglich mit NumPy-Vektorisierung über Wasser, und wenn das nicht reicht, stecke ich eine GPU rein und wechsle zu CuPy oder torch. Wenn selbst das nicht reicht, schreibe ich Native-Code mit Cython oder so ... aber auf Native zu gehen sollte man, wenn möglich, lieber vermeiden. Das ist hart.