[2023] PyO3 ile Python’ı 100 kat daha hızlı hale getirmek
(ohadravid.github.io)Son zamanlarda free-threading Python üzerine çalışırken PyO3’e ilgi duymaya başladım; yazı 2 yıllık olsa da paylaşmak istedim.
Making Python 100× Faster with <100 Lines of Rust – Özet
Arka plan
- Şirket içindeki 3-D işleme pipeline’ının temel Python kütüphanesi, eşzamanlı kullanıcı sayısındaki artış nedeniyle darboğaz oluşturmaya başladı.
- Her şeyi Rust ile yeniden yazmak çok riskli ve zaman alıcı olacağından kısmi optimizasyon tercih edildi.
Yaklaşım
- Önce ölçüm: Darboğazları belirlemek için
py-spyörneklemeli profiler kullanıldı. - Rust’un kademeli olarak devreye alınması
- Python ↔ Rust bağlantısı için
PyO3+maturinkullanıldı. - İlk olarak yalnızca
find_close_polygonsfonksiyonu Rust’a taşındı. - Ardından
Polygonveri yapısı da Rust’a taşındı ve Python’dan subclass edildi.
- Python ↔ Rust bağlantısı için
- Tekrarlanan profiler-iyileştirme döngüsü
- Gereksiz NumPy → Rust dönüşümleri en aza indirildi.
- Tahsis ve kopyalama azaltıldı, doğrudan mesafe hesaplamasıyla mikro optimizasyonlar yapıldı.
Performans değişimi
| Aşama | Ortalama çalışma süresi (ms) | İyileşme katsayısı |
|---|---|---|
| Başlangıçta saf Python | 293.41 | 1× |
v1 – yalnızca fonksiyon Rust (--release) |
23.44 | 12.5× |
v2 – Polygon da Rust |
6.29 | 46.5× |
| v3 – tahsis kaldırma ve doğrudan hesaplama | 2.90 | 101× |
Temel teknolojiler
- PyO3 : güvenli Python ↔ Rust FFI.
- maturin : build ve dağıtım otomasyonu.
- ndarray / numpy crate : Rust tarafında dizi ve lineer cebir.
- py-spy : native stack’i de gösterebilen profiler.
Çıkarımlar
- Önce profiling yapmak, küçük kod değişiklikleriyle büyük kazanımlar sağlayabilir.
- Python API’si korunurken yalnızca Rust modülünü değiştirmek bile gerçek kullanım senaryolarındaki servislere hemen uygulanabilir.
- Rust, yalnızca “performans bölgesine” ince bir katman olarak eklense bile yeterince etkili olabilir.
3 yorum
c/c++ ile Python uzantıları yapmak üretkenliği fazlasıyla düşürüyor, ama PyO3 tarafında en azından
maturinvecargoolduğu için çok rahat.Ayrıca Python modüllerinde cross compilation da şart; Rust ise cross compilation konusunda da kullanışlı.
maturin... acı...
Mümkün olduğunca NumPy vektörizasyonuyla idare edip, olmazsa GPU takıp
cupyya datorcha geçiyoruz; o da yetmezsecythonile native yazıyoruz ama... mümkünse native’e hiç girmemek daha iyi gibi. Gerçekten zor.