Mengatasi isu performa Gleam yang berusia 3 tahun dengan arena Rust
(giacomocavalieri.me)- Pada struktur
Documentrekursif di formatter Gleam, alokasi heap individual yang berulang diganti dengan referensi berbasis arena, menyelesaikan isu peningkatan performa yang tersisa selama 3 tahun Box<Self>diganti dengan&Selfyang disimpan di arena, dan pemeriksaan lifetime Rust memastikan data internal tidak direferensikan setelah arena dihapus- Ratusan
Documentyang berulang seperti keyword bahasa dan koma dialokasikan sekali saja lalu digunakan ulang, sehingga waktu eksekusi pretty printer turun dari 13ms menjadi 9,8ms, lebih cepat 24% - Total waktu eksekusi
gleam format, termasuk pembacaan source dan parsing, juga berkurang 13%, sementara penggunaan memori puncak turun sekitar 10% dari 8,4MB menjadi 7,6MB - Karena fungsi dan call site harus diubah secara luas agar meneruskan arena, diperlukan pekerjaan manual sebesar
+2963/-1032, tetapi pengurangan alokasi individual yang repetitif berhasil meningkatkan kecepatan sekaligus menurunkan penggunaan memori
Biaya alokasi pada struktur Document rekursif
- Pretty printer Gleam menggunakan struktur data
Documentrekursif yang merepresentasikan bagaimana kode dicetak dan di mana harus dipisah saat melewati batas baris - Variant utamanya terdiri dari
Stringyang berisi string untuk dicetak,Breakyang mendefinisikan string sebelum dan sesudah line break,Groupyang mengelompokkan beberapa dokumen,Nestyang menambah indentasi saat line break, dan lainnya - Daftar
[1, 2]juga direpresentasikan dengan kombinasiString,Break,Nest, danGroup- Jika muat di baris saat ini, dicetak dalam satu baris seperti
[1, 2] - Jika melewati batas baris, dipisah menjadi beberapa baris sesuai
Breakyang ditentukan, diberi indentasi, dan koma terakhir juga dicetak
- Jika muat di baris saat ini, dicetak dalam satu baris seperti
- Variant yang menyimpan
Documentlain di dalamnya, sepertiNest, sebelumnya menggunakanBox<Self>, sehingga setiap dokumen bertingkat harus dialokasikan secara individual di heap, dan pekerjaan ini bisa memakan bagian signifikan dari waktu eksekusi - Algoritme dasar pretty printer Gleam dapat dilihat di makalah Strictly Pretty
Mengganti Box dengan referensi arena
Nest(Box<Self>)yang lama diubah menjadiNest(&'doc Self), sehingga strukturnya kini mereferensikanDocumentlain yang disimpan di arena- Untuk menyimpan referensi, lifetime
'docuntuk data dokumen ditambahkan keDocument, terpisah dari lifetime string'string - Implementasinya menggunakan crate
typed_arena- Saat nilai disimpan di arena dengan
alloc, referensi ke nilai tersebut dikembalikan - Data yang dialokasikan dapat digunakan selama arena masih hidup
- Saat arena keluar dari scope, data di dalamnya juga ikut dihapus
- Saat nilai disimpan di arena dengan
- Referensi yang dikembalikan oleh
alloc(&self, value: T) -> &mut Ttidak dapat hidup lebih lama daripada arena, dan borrow checker Rust mencegah referensi tidak valid setelah arena dihapus
Mengalokasikan dokumen berulang sekali saja untuk digunakan ulang
- Arena dimanfaatkan bukan hanya untuk mereferensikan dokumen lain, tetapi juga untuk meng-cache
Documentyang berulang di seluruh kode - Dokumen yang merepresentasikan keyword bahasa seperti
String("fn"),String("pub"), danString("type")tidak perlu lagi dibuat baru setiap kali - Dokumen seperti
Break { unbroken: ", ", broken: "," }yang digunakan di antara item daftar juga dialokasikan sekali lalu digunakan ulang - Ratusan dokumen kecil yang sebelumnya harus terus dimasukkan ke
Boxkini bisa dialokasikan masing-masing hanya sekali
API sederhana dan perubahan luas pada call site
- API arena sendiri sederhana, tetapi proses memigrasikan sebagian besar kode lama membutuhkan perubahan yang repetitif
- Fungsi yang sebelumnya hanya memanggil
Box::newkini harus menerima argumen arena tambahan untuk mengalokasikan data - Fungsi seperti
format_listdiubah agar meneruskan arena yang sama ke subfungsi yang memformat item daftar, serta membuat dokumen bertingkat denganarena.allocalih-alihBox::new - Setelah beberapa hari melakukan pencarian dan penggantian secara hati-hati, pekerjaan diselesaikan dalam pull request berukuran
+2963/-1032 - Pekerjaan ini repetitif dan manual, dan LLM tidak digunakan dalam prosesnya
Peningkatan kecepatan dan penggunaan memori puncak
- Saat memformat
squirrel, sebuah proyek Gleam nyata, waktu eksekusi pretty printer turun dari 13ms menjadi 9,8ms- Jika hanya pretty printer yang dibandingkan, ini berarti peningkatan performa 24%
- Karena
gleam formatjuga membaca dan mem-parsing source code proyek selain melakukan formatting, pretty printer hanya merupakan sebagian dari keseluruhan pekerjaan, tetapi total waktu eksekusi juga berkurang 13% - Penggunaan memori puncak turun dari 8,4MB menjadi 7,6MB, berkurang sekitar 10%
- Dengan mengurangi jumlah alokasi heap individual melalui arena, formatter menjadi lebih cepat sekaligus menggunakan lebih sedikit memori
1 komentar
Pendapat di Lobste.rs
Jika ingin menurunkan penggunaan memori puncak 10% lagi, layak mempertimbangkan arena lain yang mengembalikan handle opak alih-alih referensi. Handle bisa dibuat 16-bit atau 32-bit, bukan 64-bit, sehingga efektif saat ada banyak alokasi kecil
Menurut https://donsz.nl/blog/arenas/, crate yang mendukung ini hanya
compact_arena, tetapi jika tidak sesuai kebutuhan, implementasinya sendiri juga tidak terlalu sulitOption<T>dengan memisahkan penanda keberadaan sekitar 1 bit per item danTuntuk item yang benar-benar adaSebagai gantinya, ia mengembalikan
Option<&T>alih-alih&Option<T>, dan secara lebih umum melakukan rekursi struktural dengan mengikuti referensi, bukan menaruh referensi di dalam rekursi strukturalArena layak dipakai lebih luas, dan jika mengembalikan referensi alih-alih handle atau smart pointer, pattern matching enum rekursif di Rust menjadi sangat nyaman. Biasanya Anda ingin menulis sesuatu seperti
if let MyAst::Add(MyAst::Multiply(a, b), c) = expr {}tetapi itu tidak mungkin karena ada box atau pointer di tengah; dengan referensi, pattern matching bisa dilakukan secara transparan tanpa fitur nightly. Jika semua referensi menunjuk ke satu arena, lifetime yang menghubungkan seluruh tree juga tetap konsistenPendekatan ini juga cukup mirip dengan yang didukung oleh
pretty, pustaka pretty-printing lain dengan gaya Wadler. Hanya saja, karena mendukung beberapa strategi alokasi memori, API-nya bisa terasa agak beratMengejutkan bahwa penulis makalah yang dikutip di artikel itu bekerja di perusahaan IT biasa di Brunswick