- Parser WASM yang ditulis dengan Rust secara struktural memang cepat, tetapi overhead penyalinan data dan serialisasi di batas JS-WASM muncul sebagai bottleneck performa
- Pengembalian objek langsung melalui
serde-wasm-bindgen9–29% lebih lambat dibanding serialisasi JSON, karena biaya konversi detail antar-runtime - Saat seluruh pipeline di-port ke TypeScript, pada arsitektur yang sama tercapai performa panggilan tunggal 2,2–4,6x lebih cepat
- Pada pemrosesan streaming, perbaikan O(N²)→O(N) lewat caching per kalimat menghasilkan kecepatan pemrosesan total 2,6–3,3x lebih tinggi
- Hasilnya, terkonfirmasi bahwa WASM cocok untuk komputasi intensif dengan frekuensi panggilan rendah, dan kurang cocok untuk parsing objek JS atau fungsi yang sering dipanggil
Struktur dan keterbatasan parser WASM Rust
- Parser
openui-langterdiri dari pipeline 6 tahap yang mengubah DSL yang dihasilkan LLM menjadi tree komponen React- Tahap:
autocloser → lexer → splitter → parser → resolver → mapper → ParseResult - Tiap tahap menangani tokenisasi, parsing sintaks, resolusi variabel, transformasi AST, dan lainnya
- Tahap:
- Kode Rust itu sendiri cepat, tetapi proses penyalinan string JS↔WASM, serialisasi JSON, dan deserialisasi terjadi pada setiap panggilan
- Penyalinan string input (JS→WASM), parsing di dalam Rust, serialisasi hasil ke JSON, penyalinan JSON (WASM→JS), deserialisasi di JS
- Overhead di batas ini mendominasi keseluruhan performa, dan kecepatan komputasi Rust bukanlah bottleneck
Upaya serde-wasm-bindgen dan kegagalannya
- Untuk menghindari serialisasi JSON, diterapkan
serde-wasm-bindgenyang mengembalikan struct Rust langsung sebagai objek JS - Namun, teramati 30% lebih lambat
- JS tidak bisa langsung membaca memori struct Rust, dan karena layout memori antar-runtime berbeda, dibutuhkan konversi per field
- Sebaliknya, serialisasi JSON cukup membuat string sekali di dalam Rust, lalu diproses oleh
JSON.parseyang sudah sangat teroptimasi di JS
- Hasil benchmark
Fixture JSON round-trip serde-wasm-bindgen Perubahan simple-table 20.5µs 22.5µs -9% contact-form 61.4µs 79.4µs -29% dashboard 57.9µs 74.0µs -28%
Beralih ke TypeScript dan peningkatan performa
- Struktur 6 tahap yang sama di-port sepenuhnya ke TypeScript, menghapus batas WASM dan mengeksekusi langsung di dalam heap V8
- Hasil benchmark untuk satu panggilan
Fixture TypeScript WASM Peningkatan kecepatan simple-table 9.3µs 20.5µs 2.2x contact-form 13.4µs 61.4µs 4.6x dashboard 19.4µs 57.9µs 3.0x - Hanya dengan menghapus WASM, biaya per panggilan turun drastis, tetapi inefisiensi pada struktur streaming masih tetap ada
Masalah O(N²) pada parsing streaming dan perbaikannya
- Saat output LLM dikirim dalam beberapa chunk, muncul inefisiensi O(N²) karena seluruh string akumulatif diparse ulang setiap kali
- Contoh: dokumen 1000 karakter diparse 50 kali per 20 karakter → total 25.000 karakter diproses
- Sebagai solusi, diperkenalkan incremental caching per kalimat
- Kalimat yang sudah selesai disimpan di cache, dan hanya kalimat yang masih berjalan yang diparse ulang
- AST yang sudah di-cache digabungkan dengan AST baru untuk mengembalikan hasil
- Benchmark berdasarkan keseluruhan stream
Fixture TS naif TS inkremental Peningkatan kecepatan simple-table 69µs 77µs Tidak ada contact-form 316µs 122µs 2.6x dashboard 840µs 255µs 3.3x - Semakin banyak kalimat, semakin besar efek caching, dan throughput total membaik secara linear
Pelajaran dalam penggunaan WASM
- Cocok digunakan untuk
- Pekerjaan komputasi intensif dengan interaksi minim: pemrosesan gambar/video, kriptografi, simulasi fisika, codec audio, dan sebagainya
- Porting library native yang sudah ada: SQLite, OpenCV, libpng, dan sebagainya
- Kurang cocok untuk
- Parsing teks terstruktur menjadi objek JS: biaya serialisasi menjadi dominan
- Fungsi yang sering dipanggil dengan input pendek: biaya batas lebih besar daripada komputasinya
- Pelajaran utamanya
- Pilih bahasa setelah memprofilkan lokasi bottleneck
- Pengiriman objek langsung dengan
serde-wasm-bindgenjustru lebih mahal - Perbaikan kompleksitas algoritme lebih efektif daripada pindah bahasa
- WASM dan JS tidak berbagi heap, sehingga biaya konversi akan selalu ada
Hasil akhir: Peralihan ke TypeScript dan incremental caching menghasilkan peningkatan performa 2,2–4,6x per panggilan, dan 2,6–3,3x pada keseluruhan stream
2 komentar
Bukankah maksudnya sejak awal adalah menyindir tulisan optimasi performa Rust yang terlalu tingkat tinggi..
Komentar Hacker News
Inti sebenarnya bukan TypeScript vs Rust, melainkan perbaikan algoritme streaming yang diturunkan dari O(N²) ke O(N)
Perubahan ini saja, yang dilakukan lewat caching per statement, sudah memberi peningkatan 3,3x
Terlepas dari pilihan bahasa, dari sudut pandang pengguna faktor utama peningkatan latency yang terasa ada di bagian ini
Rasanya judulnya meremehkan poin engineering menarik ini
Tulisannya sendiri menarik, tapi belakangan ini saya lelah dengan judul yang terlalu memancing klik
Metodenya mengukur waktu tiap pemanggilan lalu memakai median, tetapi di lingkungan browser ada logika pertahanan timing attack pada JS engine, jadi saya ragu soal akurasinya
Pernyataan “kami menulis ulang kode dari bahasa L ke M lalu jadi lebih cepat” itu hasil yang wajar
Karena itu menjadi kesempatan untuk membetulkan keputusan yang kusut dan keliru, sekaligus menerapkan pendekatan yang lebih baik yang baru ditemukan
Bahkan kalau L=M pun sama saja; peningkatan performa datang bukan dari bahasanya, melainkan dari proses rewrite dan redesign
Saya pernah menggali lebih dalam untuk meningkatkan performa serialisasi objek di perbatasan Rust dan JS
Pendekatan serde tampak kurang bagus dari sisi performa, dan saya merangkum percobaan untuk memperbaikinya di posting blog saya
Saya sempat penasaran kenapa Open UI tidak mengerjakan hal terkait WASM
Lalu ternyata perusahaan baru ini memakai nama Open UI, jadi membingungkan
Open UI W3C Community Group yang asli sudah lebih dari 5 tahun membuat standar seperti HTML popover, select yang bisa dikustomisasi, invoker command, dan accordion
Mereka benar-benar melakukan pekerjaan hebat
Mereka bilang mengintegrasikan serde-wasm-bindgen dalam upaya “melewati JSON round trip”, tapi akhirnya ini terlihat seperti menemukan kembali JSON dalam bentuk biner
JSON di V8 sekarang sudah sangat dioptimalkan, dan implementasi seperti simdjson bisa memproses data dalam skala gigabyte per detik
Saya rasa kecil kemungkinan JSON adalah bottleneck-nya
Saya sangat suka desain blog itu
Sidebar ‘scrollspy’ yang menyorot heading sesuai posisi scroll terutama sangat bagus
Menurut Claude, sepertinya dibuat dengan fumadocs.dev
Saya kurang paham tujuan parser Rust WASM itu
Bagian itu tidak cukup jelas di artikelnya dan perlu penjelasan lebih lanjut
Ini tampaknya untuk mencegah kebocoran informasi akibat prompt injection
Parser tersebut mengompilasi chunk yang di-stream dari LLM untuk menyusun UI secara real-time
Sebelumnya parser diulang dari awal pada setiap chunk, lalu saat diubah menjadi pemrosesan inkremental (selama porting Rust→TypeScript), performanya meningkat drastis
Saya sempat bertanya-tanya apakah TypeScript sekarang berjalan di atas basis Golang
Ini bercanda, tapi mungkin kalau ditulis ulang lagi ke Rust akan ada peningkatan performa 3x lagi /s