Bagaimana WebAssembly dapat menjalankan JavaScript dengan cepat
(bytecodealliance.org)Intro
-
Saat JS dijalankan di browser, eksekusinya cepat karena mesin JS milik browser sudah di-tuning dengan baik, tetapi belakangan ini JS juga banyak digunakan di lingkungan lain. (serverless, konsol game, iOS, dll.)
-
WASM adalah teknologi yang memungkinkan JS berjalan cepat di runtime seperti ini.
Cara kerja
-
Jika ada mesin JS, kode JS diubah menjadi bytecode melalui interpreter dan compiler JIT.
-
Di lingkungan tanpa mesin JS, mesin JS harus didistribusikan bersama kodenya, dan dengan mendistribusikan mesin JS sebagai modul WASM, ia bisa dibuat portabel di berbagai lingkungan.
-
Kode JS akan berjalan di dalam mesin JS yang terisolasi di dalam mesin WASM.
-
Mesin JS yang digunakan oleh mesin WASM adalah SpiderMonkey, yang juga digunakan oleh Firefox.
-
WASM tidak dapat menghasilkan machine code dengan sendirinya, sehingga harus melalui kompilasi sebagai JS.
-
Namun karena JIT tidak bisa digunakan, secara normal WASM seharusnya lambat. Jadi bagaimana tepatnya WASM bisa membuat eksekusi JS menjadi “cepat”?
Di mana WASM digunakan
Menggunakan JS di lingkungan iOS (atau lingkungan yang tidak bisa memakai JIT)
- Konsol game, aplikasi iOS tanpa privilege, smart TV, dll. tidak bisa menggunakan JIT karena alasan keamanan.
(→ Teks ini berbicara seolah-olah wajar bahwa kompilasi JIT punya isu keamanan, tetapi bahkan setelah dicari alasannya juga tidak terlalu jelas.)
- Karena itu di tempat seperti ini harus memakai interpreter, tetapi sebenarnya aplikasi yang berjalan di platform seperti ini biasanya hidup sangat lama dan jumlah kodenya banyak, jadi memang lebih tepat menghindari perlambatan akibat penggunaan interpreter.
- Lalu bagaimana cara tetap memakai JS sambil menghindari penurunan performa interpreter?
Menggunakan JS di serverless
- Di lingkungan serverless, JIT memang ada, tetapi masalahnya waktu cold start panjang sehingga latensi juga memanjang. (memuat engine saja minimal 5 ms)
- Ada teknik optimasi untuk menyembunyikan waktu cold start, tetapi makin baik lapisan jaringan (misalnya QUIC), makin kecil artinya, dan jika beberapa fungsi serverless dijalankan bersamaan, teknik optimasi itu juga jadi kurang berguna.
- Waktu cold start juga bisa dihindari dengan instance reuse, tetapi itu berarti state dibagikan antar-request, yang menjadi risiko keamanan.
- Karena hal-hal ini, di praktik nyata makin sering terjadi orang memasukkan banyak hal ke dalam satu fungsi serverless alih-alih mengikuti best practice.
- Artinya, jika masalah cold start saja bisa diselesaikan, berbagai teknik untuk menghindarinya tidak lagi diperlukan dan banyak masalah ikut teratasi.
- WASM membungkus dan mengisolasi JS, dan karena kode WASM sendiri pendek dan sederhana, lebih mudah diawasi serta mengurangi risiko keamanan.
Di mana mesin JS paling banyak menghabiskan waktu
Fase inisialisasi
- (inisialisasi engine) Ini terkait serverless. Engine harus menyiapkan dirinya sendiri dan menambahkan fungsi built-in ke lingkungan. Ini salah satu alasan cold start di serverless lambat.
- (inisialisasi aplikasi) Mem-parse fungsi menjadi bytecode, mengalokasikan memori untuk variabel, menetapkan nilai ke variabel
Fase runtime
- Throughput mulai tahap ini dipengaruhi oleh berbagai kondisi.
- which language features are used
- whether the code behaves predictably from the JS engine’s point of view
- what sort of data structures are used
- whether the code runs long enough to benefit from the JS engine’s optimizing compiler
Membuat mesin JS lebih cepat berarti mempercepat dua hal: fase inisialisasi dan fase runtime. Lebih tepatnya, mengurangi waktu yang dibutuhkan saat inisialisasi, dan pada runtime meningkatkan throughput, yaitu kecepatan pemrosesan kode.
Mengurangi waktu inisialisasi
-
WASM mengurangi waktu inisialisasi dengan menggunakan pre-initializer bernama Wizer. (untuk aplikasi kecil, JS on WASM kira-kira 13 kali lebih cepat dibanding JS isolate)
-
Pada tahap build sebelum kode didistribusikan, pre-initializer menjalankan semua kode JS hingga tahap inisialisasi satu kali.
-
Dengan begitu, kode JS sudah tersimpan sebagai bytecode di linear memory mesin JS, dan alokasi memorinya juga sudah selesai.
-
Lalu ini disalin apa adanya dan ditempelkan ke data section WASM.
-
-
Saat mesin JS di-instantiate, ia dapat mengakses semua data di data section. Jika membutuhkan memori tertentu, ia cukup menyalinnya dari data section. Karena itu tidak memerlukan waktu start, dan itulah sebabnya disebut pre-initialization.
-
Saat ini data section ditempelkan pada modul yang sama dengan mesin JS, tetapi ke depan direncanakan memanfaatkan module linking untuk menjadikan data section sebagai modul terpisah agar beberapa aplikasi dapat berbagi mesin JS.
-
Sebenarnya teknik pre-initialization ini juga tidak harus terbatas pada mesin JS; ini adalah konsep yang bisa diterapkan pada runtime apa pun seperti Python, Ruby, Lua, dan lain-lain.
Meningkatkan throughput
-
Jika kode JS hanya berjalan dalam waktu singkat, pada dasarnya ia juga tidak sempat melalui JIT, sehingga throughput WASM akan sama dengan browser. Namun pada kode yang berjalan lama, perbedaan throughput akibat ada atau tidaknya intervensi JIT menjadi besar.
-
Karena WASM tidak bisa memakai JIT, ia memilih pendekatan menggunakan kompilasi AOT (ahead-of-time), sambil mengambil teknik-teknik yang bisa diadopsi dari JIT.
-
Salah satu teknik optimasi JIT adalah inline caching: menyimpan potongan kode yang pernah dijalankan lalu memakainya kembali.
-
Di WASM, pola yang sering digunakan di JS dibuat lebih dulu sebagai stub. Contohnya akses ke properti objek.
-
Untuk melakukan akses properti objek dengan benar, biasanya dibutuhkan informasi shape dan offset, tetapi hal-hal ini tidak bisa diketahui lewat AOT.
-
Namun stub untuk mengakses properti dengan shape dan offset sebagai parameter bisa dibuat terlebih dahulu. Kode stub ini dapat digunakan kembali di banyak tempat.
-
-
WASM membuat semua common patterns seperti ini menjadi stub. Ini tidak bergantung pada seperti apa kode JS sebenarnya. Dengan begitu, machine code yang perlu dibuat mesin JS berkurang, waktu inisialisasi juga berkurang, dan cache locality pun membaik.
-
Dikonfirmasi bahwa hanya dengan menyiapkan stub sebesar 2kb, sekitar 95% dari kode JS nyata dapat dicakup.
-
Karena teknik seperti ini adalah optimasi ahead-of-time, yaitu mengoptimalkan tanpa mengetahui isi kode lebih dulu (tanpa profiling), jika profiling bisa dilakukan lebih jauh maka masih ada ruang untuk optimasi tambahan seperti JIT.
- Namun profiling itu sendiri tidak mudah, jadi upaya ke arah sana masih terus dilakukan.
2 komentar
Terkait isu keamanan JIT, dahulu pernah disebutkan dalam tulisan blog tim MS Edge yang pernah diperkenalkan di sini. Pada dasarnya, karena mesin JIT itu kompleks, permukaan serangannya bertambah luas, dan tampaknya metode seperti speculative optimization yang diterapkan JIT untuk meningkatkan performa cenderung berulang kali memunculkan pola masalah keamanan tertentu. Karena itu, dikatakan bahwa proporsi celah keamanan terkait JIT di antara celah keamanan browser web cukup tinggi.
https://id.news.hada.io/topic?id=4771
https://microsoftedge.github.io/edgevr/posts/Super-Duper-Secure-Mode/
https://docs.google.com/spreadsheets/d/…
Oh terima kasih! Saya malah belum sempat mencari di GeekNews