10 poin oleh GN⁺ 2025-08-05 | 1 komentar | Bagikan ke WhatsApp
  • Di mesin V8, performa fungsi JSON.stringify ditingkatkan lebih dari dua kali lipat, sehingga kecepatan serialisasi data ikut membaik
  • Dengan memperkenalkan jalur optimasi untuk objek tanpa efek samping, banyak logika pemeriksaan defensif bisa dilewati, menghasilkan peningkatan kecepatan besar pada objek data umum
  • Pada pemrosesan string, diterapkan metode tingkat lanjut dari sisi perangkat keras dan memori, seperti pembedaan 1-byte/2-byte, pemanfaatan SIMD, dan perubahan struktur buffer sementara
  • Pada proses konversi angka, algoritme Grisu3 yang lama diganti dengan Dragonbox, sehingga konversi juga menjadi lebih cepat secara umum sebelum pemanggilan Number.toString()
  • Untuk beberapa argumen dan bentuk data tertentu, proses akan kembali ke jalur serialisasi umum, tetapi dalam sebagian besar situasi pengembangan web, efek optimasi ini bisa dinikmati secara otomatis

Gambaran umum

  • JSON.stringify adalah fungsi inti untuk mengubah data menjadi string di JavaScript
  • Peningkatan performa fungsi ini juga memberi dampak positif pada pekerjaan yang sangat penting di web, seperti permintaan jaringan atau penyimpanan ke localStorage
  • Melalui rekayasa V8 terbaru, kecepatan fitur ini telah meningkat lebih dari dua kali lipat, dan berbagai teknik optimasi utamanya dijelaskan secara rinci

Jalur Fast Path tanpa efek samping

  • Inti optimasinya adalah penerapan jalur serialisasi cepat yang hanya bisa digunakan dalam kondisi tanpa efek samping (side effect)
  • Dalam kondisi seperti ini, objek ditelusuri dengan struktur iteratif alih-alih rekursif, sehingga tidak memerlukan pemeriksaan stack overflow dan memungkinkan percobaan serialisasi objek yang lebih dalam
  • Saat objek data cukup sederhana, V8 memanfaatkan Fast Path ini alih-alih logika umum yang lambat, melewati banyak pemeriksaan dan meningkatkan kecepatan

Menangani berbagai representasi string

  • V8 menyimpan string secara berbeda berdasarkan karakter 1-byte/2-byte (ASCII/non-ASCII), dan jika ada satu saja non-ASCII maka seluruhnya akan dikelola sebagai 2-byte
  • Demi performa serialisasi string, dibuat dan dikompilasi versi algoritme terpisah untuk tiap tipe string
  • Karena selama pemrosesan perlu memeriksa tipe instance string, ketika string 2-byte terdeteksi, serializer 2-byte yang sesuai akan mengambil alih status
  • Berkat ini, overhead perpindahan jalur berdasarkan encoding string pada praktiknya hampir tidak ada
  • Hasil akhirnya dibuat sebagai buffer 1-byte dan 2-byte terpisah, lalu cukup digabungkan di akhir

Optimasi serialisasi string dengan SIMD

  • String JavaScript bisa mengandung karakter yang perlu di-escape saat serialisasi JSON
  • String panjang diperiksa beberapa byte sekaligus menggunakan instruksi perangkat keras SIMD (seperti ARM64 Neon)
  • String pendek diproses dengan metode SWAR, yaitu menangani beberapa karakter sekaligus melalui operasi bit di register umum
  • Apa pun metodenya, dalam sebagian besar kasus seluruh string dapat disalin dengan cepat tanpa transformasi tambahan

Penambahan Express Lane (jalur super cepat)

  • Bahkan di dalam Fast Path, disiapkan Express Lane agar serialisasi bisa dilakukan hanya dengan menyalin key tanpa pekerjaan berulang seperti pemeriksaan properti
  • Dengan memanfaatkan flag hidden class pada objek, jika key tidak memiliki Symbol, semuanya enumerable, dan dapat diserialisasi tanpa perlu escape, maka objek ditandai sebagai fast-json-iterable
  • Saat menserialisasi objek lain dengan hidden class yang sama, penyalinan key bisa langsung dilakukan tanpa pemeriksaan tambahan
  • Teknik ini juga diterapkan di JSON.parse untuk perbandingan key yang lebih cepat

Algoritme double-to-string yang lebih cepat

  • Proses mengubah angka menjadi string juga sangat sering terjadi dan cukup kompleks
  • Dengan mengganti algoritme Grisu3 lama ke Dragonbox, peningkatan performa juga terasa pada seluruh pemanggilan Number.prototype.toString()

Optimasi struktur buffer sementara

  • Saat membangun string, sebelumnya digunakan satu buffer kontinu, sehingga ketika ruang habis akan timbul overhead karena perlu menyalin seluruh isi
  • Metode baru memakai struktur buffer tersegmentasi, yang menyambungkan beberapa buffer kecil sesuai kebutuhan
  • Karena itu, saat ruang tidak cukup, cukup mengalokasikan buffer baru tanpa perlu menyalin seluruh isi

Batasan

  • Fast Path hanya bekerja untuk serialisasi data sederhana
  • Jika tidak memenuhi kondisi berikut, jalur umum akan digunakan
    • Tidak boleh menggunakan argumen replacer atau space (tidak untuk pretty-print atau transformasi)
    • Harus berupa objek sederhana tanpa metode kustom toJSON
    • Jika ada properti berbasis indeks, akan berpindah ke jalur lambat
    • String khusus seperti ConsString tidak ditangani
  • Untuk sebagian besar penggunaan umum seperti serialisasi data, pembuatan respons API, dan caching konfigurasi, efek optimasi ini diterapkan secara otomatis

Kesimpulan

  • Dengan merombak pendekatan di seluruh area, dari desain dasar JSON.stringify hingga pemrosesan memori dan karakter, V8 berhasil mencapai peningkatan kecepatan lebih dari 2x berdasarkan benchmark JetStream2
  • Peningkatan ini bisa langsung dirasakan pada V8 versi 13.8 (Chrome 138) dan yang lebih baru

1 komentar

 
GN⁺ 2025-08-05
Komentar Hacker News
  • Terasa bahwa encoding JSON adalah hambatan besar untuk komunikasi antarproses di NodeJS

    • Pada akhirnya, kebanyakan orang mencoba memindahkan pekerjaan ke thread lain untuk mengurangi latensi event loop, tetapi kemudian menyadari bahwa beban CPU di thread utama justru menjadi 3 kali lipat
    • Saya juga sering melihat contoh yang melakukan stringify pada array satu per satu, dan tampaknya pendekatan seperti ini juga diterapkan secara internal
    • Saya berharap tim V8 bisa lebih memperkuat bagian ini
    • Saya penasaran apakah untuk beberapa set data ini bisa diproses tanpa bail out, atau bagaimana dengan masalah penanganan CString, dan saya juga tertarik apakah fitur faststr akan dihidupkan kembali
    • Saat pertama kali menganalisis performa Node tahun lalu, JSON.stringify adalah salah satu faktor terbesar yang menghambat performa layanan Node
      • Harus memakai stringify untuk key dictionary, dan apollo/express menserialisasi seluruh respons menjadi string sekaligus alih-alih melakukan streaming
      • Dari sudut pandang orang yang datang dari JVM atau Go, bagian seperti ini terasa cukup amatir di Node
    • Python juga punya masalah yang sama
      • Saya pikir akan bagus jika ada primitive IPC yang efisien di atas API tingkat tinggi untuk pola umum
    • Saya setuju dengan pendapat bahwa encoding JSON adalah hambatan besar dalam komunikasi
      • Saya penasaran seberapa besar overhead komputasi akibat pemrosesan JSON dalam komunikasi di seluruh dunia, dan apakah akan lebih baik jika bytes langsung dikirim dalam format tetap atau format yang lebih efisien untuk diparse (misalnya ASN.1)
    • Saya tidak setuju tim V8 lebih agresif memperkuat bagian ini, dan merekomendasikan para pengembang yang mengalami masalah ini untuk mencari alat lain
      • Saya rasa Node/V8 memang tidak terlalu cocok untuk backend atau masalah komputasi berperforma tinggi
      • Javascript ditujukan untuk penggunaan web dan akan tetap begitu untuk waktu yang lama, jadi tim V8 tidak perlu menyelesaikan masalah seperti ini
      • Tim Typescript juga sudah beralih ke Go, dan otomatisasi konversi antarbahasa juga dimungkinkan
    • Hampir hanya ada satu kasus di mana meng-offload pekerjaan ke Worker benar-benar menghemat waktu lebih banyak daripada biaya serialisasi/deserialisasi
      • Jika datanya besar, biaya pengiriman pesan yang besar dan mahal menjadi sebanding dengan keuntungan paralelisasi
  • Sangat mengejutkan melihat seberapa besar peningkatan performa serialisasi angka floating-point selama sekitar 10 tahun terakhir

    • Mengubah nilai floating-point IEEE menjadi string desimal UTF-8 lalu mengonversinya kembali bukan hanya lambat, tetapi juga sangat rapuh
      • Karena nilai yang dapat direpresentasikan secara tepat dalam biner dan desimal berbeda, galat kecil bisa muncul
  • Disebutkan bahwa jika JSON.stringify memiliki argumen replacer atau space, fast path tidak diterapkan

    • Kalau begitu, saya penasaran apakah memakai JSON.stringify(data, null, 0) tetap bisa memakai fast path, atau argumennya harus benar-benar undefined
  • Algoritma SWAR escaping[1] sangat mirip dengan yang pernah diimplementasikan di Folly JSON[2]

  • Saya tidak meragukan nilai pekerjaan ini sendiri, tetapi saya lebih penasaran dengan masalah konkret atau data spesifik di ekosistem V8 nyata di mana JSON.stringify benar-benar mendominasi runtime

    • Tidak harus proporsi waktu eksekusinya benar-benar mendominasi, tetapi karena ini dipanggil pada ratusan juta halaman setiap hari, efek penghematan listrik secara global pasti akan sangat besar
  • Saya rasa performa v8 belum cukup diapresiasi, JS sekarang menjadi luar biasa cepat

    • Benar-benar mengagumkan, saya rasa ini contoh bagus dari “dengan 1 miliar dolar, hampir semua masalah bisa diselesaikan”
      • Ke depan saya berharap JS terus berevolusi seperti “strict”, “stricter”, dan seterusnya agar menjadi bahasa yang lebih mudah dan sederhana untuk compile/JIT
    • Di sisi lain, v8 sudah dioptimalkan sampai sangat ekstrem sehingga mungkin hanya sekitar 100 orang di seluruh dunia yang benar-benar paham bagian dalamnya, dan kebanyakan pengembang mungkin berpikir, “kenapa JS saya tidak cepat?”
  • Saya penasaran seberapa hebat ini jika dibandingkan dengan ekosistem lain

    • Saya sudah melakukan serialisasi JSON selama lebih dari 10 tahun, tetapi hampir tidak pernah mengkhawatirkannya karena terlalu cepat
    • simdjson bisa memproses GB per detik per core, dan jika mempertimbangkan optimasi seperti prefetching/prediksi cabang, saya rasa serialisasi JSON bisa diabaikan dalam sebagian besar workload nyata
    • Kekurangan terbesar JSON adalah overhead IO; secepat apa pun serializer-nya, kalau blob 100MB tetap harus disimpan ke storage setiap kali, itu tidak banyak gunanya
  • “No indexed properties on objects” — fast path dikatakan hanya dioptimalkan untuk objek dengan key biasa berbasis string, jadi jika ada properti berindeks mirip array akan kembali ke slow path

    • Saya penasaran apa alasannya
    • Apakah itu berarti objek dengan key yang tampak seperti integer akan diserialisasi sebagai array JSON? Masa iya...?
  • Saya suka pendekatan segmented buffer; dulu kita harus menyusun sendiri trik rope dengan library userland seperti fast-json-stringify, tetapi sekarang karena native jadi jauh lebih baik

    • Saya penasaran seberapa sering orang mengalami kondisi bailout (misalnya replacer, space, .toJSON() kustom), dan apakah dalam kasus seperti itu langsung fallback ke jalur lambat
  • V8 luar biasa, tetapi mungkin karena JS itu sendiri, performanya masih kalah dibanding LuaJIT atau JVM

    • JVM memang punya waktu warming-up yang panjang, tetapi tetap saja lebih baik daripada JS
    • Penyebabnya memang JS; saya rasa V8 jauh lebih maju daripada luajit dan JVM
      • Java punya lebih sedikit batasan real-time (ada compiler), dan itu kelebihannya
    • Banyak overhead JS berasal dari sifatnya yang dinamis
      • asm.js melarang perilaku dinamis seperti perubahan shape objek, sehingga banyak pengecekan bisa dilewati
    • Saya keberatan dengan ungkapan “bahkan JVM”; JVM itu termasuk yang terbaik