[2023] Membuat Python 100× Lebih Cepat dengan PyO3
(ohadravid.github.io)Akhir-akhir ini saya tertarik pada PyO3 saat mempelajari free-threading Python, jadi saya membagikan tulisan ini meski sudah berusia 2 tahun.
Making Python 100× Faster with <100 Lines of Rust – Ringkasan
Latar belakang
- Library Python inti untuk pipeline pemrosesan 3-D internal perusahaan menjadi bottleneck seiring meningkatnya jumlah pengguna bersamaan.
- Menulis ulang semuanya ke Rust terlalu berisiko dan memakan waktu, jadi dipilih optimasi parsial.
Pendekatan
- Mulai dari pengukuran: mengidentifikasi bottleneck dengan profiler sampling
py-spy. - Mengadopsi Rust secara bertahap
- Menghubungkan Python ↔ Rust dengan
PyO3+maturin. - Pertama, hanya mem-porting fungsi
find_close_polygonske Rust. - Lalu, memindahkan struktur data
Polygonke Rust juga, sambil melakukan subclassing dari Python.
- Menghubungkan Python ↔ Rust dengan
- Profiling dan perbaikan berulang
- Meminimalkan konversi NumPy → Rust yang tidak perlu.
- Mengurangi alokasi dan penyalinan, lalu melakukan micro-optimization dengan perhitungan jarak langsung.
Perubahan performa
| Tahap | Waktu eksekusi rata-rata (ms) | Faktor peningkatan |
|---|---|---|
| Awal, Python murni | 293.41 | 1× |
v1 – Hanya fungsi di Rust (--release) |
23.44 | 12.5× |
v2 – Polygon juga di Rust |
6.29 | 46.5× |
| v3 – Menghapus alokasi, perhitungan langsung | 2.90 | 101× |
Teknologi inti
- PyO3 : FFI Python ↔ Rust yang aman.
- maturin : otomatisasi build dan distribusi.
- ndarray / numpy crate : array dan aljabar linear di sisi Rust.
- py-spy : profiler yang dapat melihat hingga native stack.
Pelajaran
- Jika profiling dilakukan terlebih dahulu, perubahan kode kecil bisa memberi keuntungan besar.
- Bahkan jika hanya mengganti modul Rust sambil mempertahankan API Python, ini tetap bisa langsung diterapkan ke layanan produksi.
- Rust tetap sangat efektif meski hanya diperkenalkan secara tipis pada “area performa”.
3 komentar
Membuat ekstensi Python dengan C/C++ terlalu menurunkan produktivitas, tetapi PyO3 sangat nyaman karena setidaknya ada
maturindancargo.Selain itu, cross-compilation juga wajib untuk modul Python, dan Rust juga memudahkan cross-compilation.
maturin... menyakitkan...
Sebisa mungkin bertahan dengan vektorisasi NumPy; kalau tidak bisa, pasang GPU lalu ganti ke CuPy atau Torch, dan kalau itu pun masih tidak cukup, tulis kode native dengan Cython... tapi sepertinya sebaiknya sebisa mungkin hindari native. Capek.