12 points par darjeeling 2025-05-23 | 3 commentaires | Partager sur WhatsApp

Je m’intéresse à PyO3 en étudiant récemment le free-threading de Python, donc je partage cet article qui date de deux ans.

Making Python 100× Faster with <100 Lines of Rust – résumé

Contexte

  • La bibliothèque Python centrale d’un pipeline interne de traitement 3-D créait un goulot d’étranglement avec l’augmentation du nombre d’utilisateurs simultanés.
  • Réécrire l’ensemble en Rust était trop risqué et trop long, donc le choix s’est porté sur une optimisation partielle.

Méthode

  1. Mesurer d’abord : identification du goulot d’étranglement avec le profiler d’échantillonnage py-spy.
  2. Introduction progressive de Rust
    • Connexion Python ↔ Rust avec PyO3 + maturin.
    • Portage d’abord de la seule fonction find_close_polygons en Rust.
    • Puis déplacement de la structure de données Polygon en Rust, avec sous-classage depuis Python.
  3. Boucle profilage-amélioration
    • Réduction au minimum des conversions inutiles de NumPy → Rust.
    • Diminution des allocations et des copies, avec micro-optimisations via calcul direct des distances.

Évolution des performances

Étape Temps d’exécution moyen (ms) Facteur d’amélioration
Python pur initial 293.41
v1 – fonction seule en Rust (--release) 23.44 12.5×
v2 – Polygon aussi en Rust 6.29 46.5×
v3 – suppression des allocations, calcul direct 2.90 101×

Technologies clés

  • PyO3 : FFI sûre entre Python et Rust.
  • maturin : automatisation du build et du déploiement.
  • ndarray / numpy crate : tableaux et algèbre linéaire côté Rust.
  • py-spy : profiler capable d’afficher aussi la pile native.

Enseignements

  • En profilant d’abord, on peut obtenir de gros gains avec de petites modifications de code.
  • Même en conservant l’API Python, remplacer uniquement le module Rust permet une application immédiate en production.
  • Rust est déjà très efficace même lorsqu’il n’est introduit que de façon ciblée sur la zone de performance.

3 commentaires

 
allinux 2025-05-23

Créer des extensions Python en C/C++ fait trop chuter la productivité, alors que PyO3 est vraiment pratique grâce à maturin et cargo.
De plus, la compilation croisée est indispensable pour les modules Python, et Rust la rend également simple.

 
lamanus 2025-05-23

maturin... quelle galère...

 
aer0700 2025-05-23

On tient au maximum avec la vectorisation NumPy, et si ça ne suffit pas, on branche un GPU et on passe à CuPy ou Torch ; si ça ne suffit toujours pas, on écrit du natif avec Cython… mais à mon avis, mieux vaut éviter le natif autant que possible. C’est difficile.