- WebAssembly berhasil membawa program C++ besar seperti Photoshop ke web, tetapi penyebarannya terbatas pada aplikasi berpusat DOM karena model pemrogramannya berbeda dari JavaScript
- Dukungan Wasm GC dan reference types di browser membuka peluang bagi bahasa dengan memori terkelola seperti Python dan Scheme, tetapi di web, ukuran transfer langsung menjadi hambatan adopsi
- Program Wasm sederhana di Go bisa berukuran 2MB, dan saat import ditambahkan dapat melampaui 10MB; Pyodide REPL mengunduh sekitar 20MB, sehingga memberatkan untuk aplikasi web umum
- Kompiler Hoot Scheme menargetkan Wasm dengan dukungan GC dan telah memangkas unit kompilasi “main” minimal hingga sekitar 70KB, sementara unit kompilasi tambahan bisa kurang dari 1KB
- Tree shaking yang efektif bukan sekadar menghapus fungsi yang tidak direferensikan, melainkan masalah kompiler yang memadukan analisis alur dan desain pustaka standar
Area WebAssembly Berhasil di Web
- WebAssembly belum menyebar seluas ekspektasi awal di web, tetapi meraih keberhasilan terbatas di area tertentu
- Contoh utamanya adalah kasus membawa program C++ besar seperti Photoshop ke web
- Figma juga disebut sebagai contoh Wasm dari 5 tahun lalu, tetapi sekarang tidak terlalu menonjolkan Wasm
- Banyak pustaka NPM kecil yang dikompilasi dari C++ atau Rust menggunakan Wasm secara internal
- Blazor mungkin digunakan di sebagian aplikasi perusahaan internal, meski bisa jadi dilebih-lebihkan oleh pemasaran
- Demo 3D FPS Unreal Engine adalah eksperimen berbasis rilis besar sebelum 5 tahun lalu, dan Unreal 5 saat ini tidak mendukung target WebAssembly
Mengapa Wasm Terhambat di Aplikasi Berpusat DOM
- WebAssembly berhasil di lingkungan di luar web dan bisa makin penting di platform web, tetapi di web dapat dilihat sebagai baru saja melewati jurang kekecewaan
- Wasm unggul pada pekerjaan yang tidak dikerjakan JavaScript dengan baik atau pekerjaan yang membutuhkan implementasi bersama antara klien dan server
- Wasm belum berhasil di aplikasi berpusat DOM
- Tidak ada pembicaraan untuk menulis ulang frontend wordpress.com dengan Wasm
- Model pemrograman utama web adalah JavaScript dengan tipe dinamis dan memori terkelola
- WebAssembly 1.0 dirancang berpusat pada tipe statis dan memori linear
- Mengakses DOM dari Wasm cukup merepotkan hingga hanya pendukung Wasm yang sangat antusias yang sanggup menanganinya
- Bahasa seperti C# harus menyertakan garbage collector, dan hal ini menjadi hambatan dalam adopsi Wasm untuk bahasa selain C/Rust
Masalah Ukuran Transfer yang Tetap Ada Setelah Wasm GC
- Browser akan menyediakan dukungan reference types dan garbage collection dalam beberapa bulan ke depan
- Chrome dan Firefox sudah menyediakan Wasm GC
- Safari juga diperkirakan tidak jauh lagi berkat pekerjaan Asumu Takikawa
- Wasm GC adalah perubahan yang mendorong lebih banyak bahasa memperbarui toolchain agar mendukung WebAssembly
- Agar Wasm di web berhasil, kompiler harus menghasilkan kode kecil
- Jika toolchain bahasa dapat membuat file Wasm yang berguna dengan ukuran beberapa KB dalam hal transfer, itu menguntungkan
- Jika tidak, mereka harus bergantung pada ekspektasi yang berlebihan atau basis pengguna yang terkunci, dan dapat bertahan dalam keseimbangan rapuh sampai solusi berikutnya ditemukan
- Di ekosistem JavaScript, industri alat untuk mengurangi ukuran pengiriman dan bloat sudah besar
- Bundler seperti esbuild menggabungkan beberapa modul JS menjadi satu file
- Mereka berusaha hanya menyertakan fungsi dan tipe data yang digunakan
- Strategi pengurangan ukuran seperti minification, yang memendekkan nama, juga diterapkan
Jebakan Nama Tree Shaking
- Tree shaking memiliki metafora visual bahwa hanya kode yang dibutuhkan pada suatu halaman yang dipertahankan dan sisanya dijatuhkan
- Dalam metafora ini, modul dibayangkan seperti cabang dan definisi seperti daun, tetapi pohon nyata tidak memberi tahu cabang mana yang diperlukan dan tidak diperlukan saat batangnya diguncang
- Nama itu sendiri mendorong orang berpikir ke arah menghapus kode yang tidak diperlukan, tetapi dari sudut pandang algoritme, lebih tepat mencari titik tetap yang hanya mempertahankan kode yang diperlukan
- Meski begitu, tree shaking adalah nama yang berkesan, sehingga tetap dipakai walaupun tidak akurat secara hortikultura maupun algoritmik
Hambatan Ukuran dari Runtime yang Tebal
- Pada bahasa dengan runtime tebal, tree shaking maksimal bukanlah prioritas besar
- Dalam dukungan WebAssembly Go, program paling sederhana sekalipun berukuran 2MB menurut golang wiki
- Jika import ditambahkan, ukurannya bisa melampaui 10MB
- Contoh REPL Pyodide, port Python WebAssembly, mengunduh sekitar 20MB data
- Ukuran seperti ini baik untuk demo teknologi atau aplikasi yang sangat kaya, tetapi sulit menjadi pilihan pengembangan web yang umum
Toolchain Alternatif dan Implementasi yang Disesuaikan dengan Platform
- Dukungan Wasm bawaan Go dan Pyodide berasal dari toolchain upstream, dan di server ukuran biner mungkin tidak terlalu penting
- Saat menargetkan perangkat kecil, muncul implementasi terpisah
- Backend Wasm TinyGo tampaknya bisa turun hingga kurang dari 1KB
- Toolchain alternatif seperti ini sering disertai batasan atau kekhasan
- Program Python yang menargetkan Wasm dan berjalan di lingkungan DOM mau tidak mau akan berbeda dari program Python “native”
- Penulis toolchain berusaha menyediakan bahasa yang sama, tetapi implementasi pustaka standar bisa berbeda
- Pengembang ClojureScript juga mungkin ingin menghapus dokumen perbedaan dengan Clojure jika memungkinkan, dan jika Wasm menjadi target praktis untuk ClojureScript, kemungkinan itu terbuka
Cara Tree Shaking Hoot Scheme
- Setelah dukungan GC, Wasm memungkinkan kita memikirkan pemrograman DOM dengan bahasa seperti Python, tetapi penggunaan massal membutuhkan modul kecil
- Hoot Scheme compiler menargetkan Wasm dengan GC
- Unit kompilasi “main” minimal saat ini sekitar 70KB
- Mereka menargetkan ukuran yang lebih kecil
- Unit kompilasi tambahan yang mengimpor fitur runtime seperti exception handler dari modul main bisa kurang dari 1KB
- Kompiler Hoot menempelkan prelude di depan kode pengguna
- Tree shaking terjadi dalam beberapa tahap
- Evaluasi parsial dapat mengevaluasi efek binding yang tidak digunakan lalu menghapusnya
- fixing letrec juga melakukan pekerjaan serupa
- CPS sering menelusuri program dan hanya mengikuti fungsi, nilai, dan edge alur kontrol yang direferensikan
- Pass dead-code elimination eksplisit menghapus assignment tanpa efek yang tidak digunakan dan mungkin muncul setelah optimasi lain
- Definisi dalam pustaka standar yang ditulis dalam WebAssembly yang mendekati raw hanya disertakan dalam biner hasil jika diperlukan
Penghapusan yang Mudah dan yang Sulit
- Definisi prosedur seperti fungsi atau closure relatif mudah ditangani
- Cukup menyertakan fungsi yang direferensikan oleh kode
- Dalam bahasa seperti Scheme, ini saja sudah memberi dampak cukup besar
- Ada tiga kesulitan yang segera terlihat
-
Model evaluasi letrec*
- Scope definisi prelude bersifat rekursif tetapi memiliki urutan
- Nilai binding dapat memanggil atau mereferensikan nilai yang didefinisikan sebelumnya, dan juga dapat menangkap nilai yang akan didefinisikan kemudian
- Jika evaluasi nilai binding harus mereferensikan nilai yang baru didefinisikan kemudian, itu adalah error
- Pada prosedur biasanya tidak menjadi masalah, tetapi pada definisi non-prosedur, kompiler mungkin tidak dapat membuktikan sifat “hanya mereferensikan binding sebelumnya”
- Dalam kasus ini, algoritme fixing letrec reloaded dapat menyisakan binding yang di-
set!, dan untuk menghapusnya diperlukan pass DCE yang halus
-
vtable pada tipe record
- Beberapa definisi non-prosedur adalah tipe record
- Tipe record memiliki vtable yang memuat cara menampilkan record atau cara memeriksa instance, dan sebagainya
- Callback vtable dapat membuat banyak kode tetap hidup meskipun sebenarnya tidak digunakan
-
Fungsi output polimorfik
- Fungsi polimorfik seperti
displaymemperluas cakupan kode yang diperlukan secara besar - Saat memanggil
displayuntuk mencetak string, seluruh fasilitas I/O buffered ikut terbawa - Karena
displaydapat mencetak apa saja, kode untuk berbagai kasus seperti bitvector dan pair juga bisa ikut tertarik - Jika memanggil
write-stringyang hanya menggunakan string, kode output data umum dapat dihindari, tetapi fasilitas I/O buffered umum seperti port tetap disertakan
- Fungsi polimorfik seperti
Tree Shaking Optimal adalah Masalah Analisis Alur
- Tree shaking yang optimal pada akhirnya adalah masalah analisis alur
- Jika program tidak pernah memiliki bitvector, kode yang menangani bitvector di dalam
displaybisa menjadi kode mati - Untuk mengetahui ini, perlu diketahui jenis argumen apa yang digunakan saat
displaydipanggil, dan untuk itu diperlukan analisis alur tingkat tinggi - Di Python, masalahnya menjadi lebih sulit
- Dispatch berorientasi objek adalah pemrograman tingkat tinggi, sehingga arti
foo.barbergantung pada apa itufoo - Lookup Python lebih dinamis daripada Scheme, dan metode seperti
__getattr__dapat digunakan di berbagai tempat - Dalam praktiknya, analisis alur mungkin dapat mengecualikan lookup dinamis seperti ini
- Target tree shaking Python bukanlah term besar dengan binding leksikal, melainkan kumpulan modul yang kompleks
- Ini mirip dengan JavaScript, tetapi Python tidak memiliki ekosistem bundler tree shaking yang mapan
- Dispatch berorientasi objek adalah pemrograman tingkat tinggi, sehingga arti
Syarat Toolchain Bahasa Wasm untuk Web
- Wasm GC dapat memungkinkan pemrograman DOM dengan bahasa selain JavaScript
- Agar berujung pada penggunaan massal, modul Wasm hasilnya harus kecil
- Setiap toolchain bahasa membutuhkan investasi besar
- Investasi seperti ini sering muncul sebagai toolchain alternatif yang mencakup algoritme tree shaking eksperimental
- Pustaka standar alternatif perlu dirancang agar tree shaker dapat bekerja lebih baik
1 komentar
Opini Hacker News
Blob Wasm openEtG (mesin permainan kartu) dijaga tetap di bawah 400KB, sambil memindahkan banyak logika seperti pembuatan teks kartu ke Wasm, dan ditulis dengan Rust
Untuk mengurangi ukuran, diperlukan pengelolaan seperti memakai aritmetika fixed-point alih-alih floating-point, beralih dari hashmap ke vektor, menghindari string, memakai allocator kecil seperti
talc, dan mengurangi dependensiSaat ini hanya memakai
randdanfxhash, tetapirandtampaknya juga bisa dihapus;fxhashhanya digunakan sebagai hash status game untuk memeriksa apakah terjadi desinkronisasiJenis instans generik juga dikurangi; karena
Vecsudah ada, tipe sepertiBox<[i16]>tidak ikut ditarik masuk, dan menghilangkan floating-point serta hashmap juga membantu mengurangi keragaman tipeAlgoritmenya juga dirancang dengan mempertimbangkan ukuran; misalnya, mekanisme adrenaline, di mana makhluk dengan attack rendah menyerang lebih sering, dienkode dengan lookup table yang dipaketkan dalam bit
Biaya menyimpan nilai yang tidak dikompresi dibandingkan dengan biaya logika decoding, dan evaluasi AI memakai presisi tetap 6-bit karena 64 dienkode lebih efisien daripada 128 di WebAssembly
Mekanisme targeting sebelumnya berbentuk AST, dengan tiap predikat sebagai enum dan AND/OR sebagai slice ekspresi, tetapi sekarang ekspresi dienkode ke integer 32-bit dalam notasi Polandia; AND/OR dimuat dalam 2 bit dan predikat dalam 6 bit
Di sini notasi Polandia lebih baik daripada notasi Polandia terbalik, karena memungkinkan evaluasi short-circuit untuk AND/OR
Di tempat kerja, saya sedang mempertimbangkan apakah fixed-point akan membantu untuk masalah yang kebutuhan resolusi maksimumnya sudah diketahui, misalnya ketika tidak perlu presisi posisi di bawah milimeter, jadi saya ingin mendengar lebih banyak isu terkait
wasm-optdari binaryenTampaknya ini secara konsisten mengurangi ukuran Wasm sekitar 20–30%: https://github.com/WebAssembly/binaryen
Saat menyajikan bundel Wasm ke browser, sebaiknya juga memakai kompresi Brotli dan mengatur web server agar menggunakan file terkompresi Brotli
Jika memakai nginx, ini bisa dilakukan dengan perubahan satu baris, dan Brotli mengurangi ukuran bundel Wasm sekitar 3 kali lipat serta jauh lebih baik daripada gzip
Ukuran itu sudah mencakup forward/backward pass deep learning, algoritme reinforcement learning, hingga simulasi dinamika
Sekarang pun belum sampai memberatkan, tetapi saya penasaran seberapa kecil lagi bisa dibuat, jadi ingin segera mencoba mengecilkannya
Saya ingin memahami bagaimana pernyataan bahwa menghindari floating-point dan memakai aritmetika fixed-point dapat menghemat ruang bisa masuk akal
Khususnya, cara memakai
VecsepertiBoxseperti pada poin 6 sepertinya tidak akan menghasilkan penghematan besarNama tree shaking tampaknya merupakan istilah yang cukup keliru
Compiler Virgil menyebutnya “analisis keterjangkauan” dan menanamkannya ke dalam model kompilasi
Compiler mem-parse program dan kode pustaka, melakukan pemeriksaan tipe, serta menjalankan kode inisialisasi, tetapi setelah itu hanya menganalisis kode yang dapat dijangkau dengan menelusuri dari entry point utama dan memasukkannya ke binary akhir
Program yang hanya memiliki satu fungsi
maintanpa sistem runtime pun dapat dibuat dengan baik, dan sistem runtime hanya diperlukan untuk stack trace dan garbage collection sehingga bisa dihilangkan jika diinginkanContoh pertama yang saya temukan adalah alat Treeshaker pada Lucid Common Lisp 4.1 tahun 1992, sebuah implementasi Common Lisp komersial untuk UNIX
Lucid CL memiliki konsep image, yaitu dump memori tersimpan dari heap Lisp yang sedang berjalan, dan aplikasi terdiri dari image serta runtime
Image biasanya berisi hampir semua kode dan data di memori, sehingga muncul kebutuhan membuat image yang lebih kecil untuk distribusi, dan Treeshaker menghapus kode serta data yang dinilai “tidak digunakan” sebelum image disimpan
Caranya adalah memangkas koneksi pada graf data dan kode Lisp yang dapat dijangkau, lalu GC atau kode khusus mengumpulkan sampah untuk mengurangi memori sebelum men-dump-nya menjadi image yang lebih kecil
Jadi Treeshaker bukan alat compiler, melainkan alat untuk menghapus kode dan data yang tidak digunakan dari heap Lisp
Image Lisp dasar mencakup compiler, interpreter, bahkan implementasi REPL, sehingga jika program yang sedang berjalan diinterupsi untuk masuk ke REPL, semua kode dalam heap yang dipulihkan dari image masih dapat digunakan
Karena itu, masuk akal untuk menghapus compiler atau REPL sekalipun
Analisis keterjangkauan sering dipakai untuk menentukan kode mana yang dapat dihapus, tetapi analisis itu sendiri tidak menghapus kode; penghapusan adalah tahap lanjutan
“Tree shaking” biasanya mengisyaratkan penghapusan pada tingkat fungsi, sedangkan dead-code elimination dapat dilakukan pada tingkat yang jauh lebih halus, seperti menghapus cabang ekspresi kondisional, dan dapat didasarkan pada berbagai analisis statis
Saat pertama melihatnya, saya langsung memahami maknanya tanpa perlu mencari tahu lebih lanjut
Maksudnya mengguncang pohon agar benda yang menempel longgar jatuh, dan di sini jelas berarti paket yang tidak digunakan “terguncang hingga jatuh” dari tree
Jika diagram kode sumber dibayangkan sebagai objek fisik, bagian-bagian yang tidak dapat dijangkau dari root akan terlepas saat diguncang
Tidak jauh berbeda dari analisis keterjangkauan; salah satu ungkapannya hanya lebih memicu penalaran spasial
Sebagian kode tidak terhubung ke batang yang disebut entry point
Tree shaking menghapus bagian yang tidak terhubung, yaitu daun yang longgar dan cabang yang mati
Jika toolchain compiler suatu bahasa dapat membuat Wasm berguna berukuran hanya beberapa KB untuk ukuran transfer jaringan, kemungkinan baru akan terbuka
Binary yang sangat kecil akan membuka use case baru untuk Wasm, dan WasmGC jelas membantu
Java dan Kotlin pun saat ini bisa cukup baik, sekitar 2–3KB: https://developer.chrome.com/blog/wasmgc, https://twitter.com/bashorov/status/1661377260274720770
Namun perlu berhati-hati karena kode besar bisa ikut tertarik tergantung API apa yang digunakan
Meski begitu, berkat WasmGC bahasa-bahasa ini tidak perlu memasukkan beberapa KB kode manajemen memori ke dalam bundle, sehingga dari sisi ukuran kode sudah jauh lebih baik daripada C++ dan Rust
Saat menangani objek JavaScript, ini bisa membantu, dan mungkin juga dapat dipakai sebagai allocator memori alternatif yang kurang efisien
Namun allocator Rust bawaan di Wasm kemungkinan besar sudah memadai dalam kebanyakan kasus
Jika mulai mengoptimalkan ukuran, memakai
wasm-opt, dan mengompres dengan Brotli, kita bisa memasukkan kode dalam jumlah sangat besar ke ukuran unduhan di bawah 100KBMembandingkan biaya 100KB Wasm secara langsung dengan 100KB JavaScript bundle adalah keliru, karena parsing dan inisialisasi JavaScript beberapa kali lebih lambat
Waktu unduh memang biaya nyata, tetapi time to first paint untuk 100KB Wasm jauh lebih baik daripada 100KB JavaScript
Tetap saja, makin kecil makin baik, dan menarik melihat Java, Kotlin, C#, Python, Go, dan lainnya menjadi bahasa yang praktis untuk aplikasi web
Saya juga penasaran seperti apa ukuran aplikasi nyata nantinya
Perbedaan terbesar kemungkinan akan berasal dari desain framework, dan diffing virtual DOM pasti selalu lebih rumit dan lambat daripada library komponen reaktif seperti Svelte, SolidJS, dan Leptos dari Rust
Jika WasmGC didukung di mana-mana, pilihan framework web tampaknya akan jauh lebih berpengaruh pada performa daripada bahasa yang dipakai
Saya meragukan apakah pernyataan “Wasm membuat pemrograman DOM dalam bahasa selain JavaScript menjadi sesuatu yang dapat dibayangkan” benar-benar tepat
Sejauh yang saya tahu, untuk memanipulasi DOM dari bahasa seperti Rust, pada akhirnya tetap diperlukan binding yang menserialisasi pemanggilan yang akan dijalankan di sisi JavaScript
Dalam bentuk Wasm saat ini, rasanya masih terikat pada JavaScript
Secara teori, sekarang runtime bisa mengimpor fungsi DOM dan memanggilnya dengan referensi objek DOM, sehingga melewati JavaScript dan memanggil runtime secara langsung
Saya tidak tahu pasti apakah ini benar-benar bisa dilakukan, tetapi GC setidaknya menyediakan mekanisme prasyarat untuk menuju titik itu
Pada dasarnya mirip SolidJS
#[component] fn App() -> impl IntoView { let (count, set_count) = create_signal(0); view! { "Click me: "{move || count()} } }Dibanding JavaScript, ada overhead ukuran Wasm, tetapi tidak parah
Setelah
wasm-optdan kompresi Brotli, bundle Wasm untuk aplikasi counter ini berukuran 37KB; masih di kisaran yang mirip React, tetapi jauh lebih cepat setelah dijalankanSaya belum mencoba manipulasi DOM langsung, tetapi untuk komponen umum terlihat bagus
Misalnya, DOM yang diimplementasikan dalam Rust bisa digunakan dari modul Wasm yang ditulis dalam Rust tanpa menjalankan JavaScript
Namun, ini rumit karena ada bagian DOM API yang dispesifikasikan mengikuti semantik JavaScript, jadi tampaknya mereka memulai dari hal-hal yang warisan JavaScript-nya lebih sedikit, seperti request HTTP, soket TCP, dan akses filesystem
Istilah “dead code elimination” sudah ada sejak lama; saya penasaran kenapa ungkapan tree shaking muncul
“Tree shaking” merujuk pada analisis seluruh program yang membuang seluruh modul dan fungsi yang tidak dipanggil
Secara konseptual memang sama, tetapi penulis compiler biasanya harus mengimplementasikannya secara terpisah, jadi adanya dua nama cukup membantu
Tree shaking mudah diasosiasikan, lebih enak diucapkan daripada “dead code elimination”, dan lebih mudah didekati, jadi tampaknya menjadi istilah yang lebih populer
Saya mencoba mencari istilah mana yang muncul lebih dulu; penggunaan paling awal “dead-code elimination” yang saya temukan ada di makalah tahun 1973: https://research-repository.st-andrews.ac.uk/bitstream/handle/10023/22636/NicholasAlexandrakisMScThesis1973_original_C.pdf?sequence=1
Di Google Scholar, saya tidak menemukan penggunaan “tree shaking” atau “tree shaker” di bidang komputasi; kebanyakan terkait pepohonan seperti pohon jeruk
Diskusi paling awal yang tampak saya temukan adalah posting di comp.lang.lisp: https://groups.google.com/forum/#!topic/comp.lang.lisp/pspFr1XByZk
Saya rasa metafora tree shaking mungkin berasal dari salah satu cara memanen pohon buah
Saat pohon diguncang, buah yang matang akan jatuh
Namun, ini bukan metafora yang sangat bagus karena dalam panen buah yang diinginkan adalah yang jatuh, sedangkan saat menyimpan image yang diserialisasi, yang jatuh justru dibuang
Buah lebih rapuh, sehingga jika jatuh terlalu jauh bisa memar, jadi biasanya dipetik dengan tangan
Proses mengguncang memang cukup kasar, tetapi tidak merusak pohonnya
Filosofi saya adalah mengoptimalkan ukuran dan performa secara serius di JavaScript melalui penyederhanaan algoritma dan desain, sehingga ada ruang ukuran bundle dan CPU untuk kode yang membutuhkan komputasi brute-force
Di proyek ClubCompy, kami menggunakan Wasm untuk mengimplementasikan sistem berkas FAT di atas local storage, dan ternyata biaya komputasinya sangat besar
Nanti tahun ini, saat memasukkan kembali deteksi tabrakan sprite yang akurat per piksel, saya juga berencana memakai Wasm
Implementasi pertama murni JavaScript, dan ketika 256 sprite di layar saling bertabrakan, framerate turun di bawah 1fps
Saya kira ini bisa dibuat tanpa dampak performa dengan memprosesnya nyaris gratis di worker thread
Ada alasan mengapa sebagian besar game 2D memakai kotak tabrakan persegi: pemain lebih mudah memprediksi apakah akan terjadi tabrakan
Dengan tabrakan per piksel, gerakan yang sama bisa bertabrakan atau tidak tergantung fase siklus animasi
Rasanya tidak enak jika gerakan yang sebelumnya selalu berhasil tiba-tiba gagal hanya karena timing animasinya kebetulan buruk
Selain itu, per piksel juga tidak selalu realistis; detail kecil pada sprite bisa saja berupa kain atau rambut yang dalam kenyataannya tidak menimbulkan tabrakan keras
Sprite juga sering bergerak beberapa piksel per frame, sehingga tabrakan piksel individual justru meningkatkan kemungkinan saling menembus
Deteksi tabrakan yang sederhana dan dapat diprediksi umumnya adalah pilihan terbaik
1fps seharusnya baru muncul pada sekitar 20 ribu tabrakan
Untuk per-piksel, jika memori cukup longgar, cara sederhananya adalah merender canvas offscreen untuk seluruh arena, menggambar tiap sprite sebagai stencil dengan warna berbeda, lalu memeriksanya
Ini berjalan dalam waktu linear dan tidak memerlukan pemartisian terpisah
Namun, karena mitigasi anti-fingerprinting dapat mengubah bit rendah data canvas menjadi noise, mungkin perlu memakai bit tinggi
Tulisan ini mengatakan hal yang benar. Wasm punya masalah ukuran kode
Di browser, ini jadi masalah karena semua kode harus diunduh sebelum situs mulai berjalan; di arsitektur serverless pun ini jadi masalah karena kode dimuat dari cold storage ke server tertentu saat dibutuhkan, sementara klien menunggu
Tree shaking bisa membantu, tetapi sepertinya hanya akan menjadi optimisasi bertahap
Pada dasarnya, alasan program Wasm menjadi besar adalah karena masing-masing harus membawa seluruh runtime bahasa dan pustaka standarnya sendiri
Sebaliknya, JavaScript mendapatkan implementasi dan pustaka dasar dari browser
Memang mudah berpikir bahwa browser tidak mungkin memuat semua runtime bahasa sejak awal, tetapi ada pendekatan lain yang perlu dipertimbangkan: shared library dan dynamic linking
WebAssembly mendukung dynamic linking, dan beberapa modul Wasm dapat dimuat bersamaan serta saling memanggil
Namun banyak toolchain Wasm enggan mendukung ini, dan dirancang untuk menautkan secara statis seluruh program beserta runtime bahasa ke dalam satu modul raksasa
Pyodide (CPython on Wasm) adalah contoh tandingannya; saat ini ia dirancang dengan mempertimbangkan dynamic linking
Inilah yang membuat Cloudflare Workers baru-baru ini dapat menambahkan Python sebagai dukungan kelas satu: https://blog.cloudflare.com/python-workers
Dari sudut pandang tech lead untuk seluruh platform Workers, semua Workers yang berjalan di mesin yang sama berbagi satu runtime Pyodide terkompilasi, sehingga tidak perlu dimuat terpisah untuk setiap Worker
Jika dynamic linking didukung lebih luas, kita bisa membayangkan struktur di mana browser melakukan preload runtime bahasa populer, bahkan pustaka populer, lalu semua halaman web yang membutuhkan runtime tersebut berbagi salinan kode read-only yang sama
Runtime ini tetap berjalan di dalam sandbox, jadi browser tidak perlu mempercayainya; cukup menyediakannya saja
Dengan begitu, kita dapat membuat browser yang “secara bawaan” mendukung bahasa selain JavaScript tanpa pengelola browser harus sepenuhnya memverifikasi atau memikirkan implementasi bahasanya
Saya tidak terlalu paham, tetapi setahu saya pendekatan itu tidak berjalan baik
Ada terlalu banyak versi pustaka sehingga versi individualnya tidak benar-benar digunakan secara luas, lalu kemudian karena kekhawatiran privasi, browser bergerak ke arah memisahkan cache per situs atau per origin
Mungkin ini bukan soal caching, tetapi ini masalah rumit yang lebih dekat ke masalah sosial daripada masalah teknis
Jika lebih banyak runtime bahasa dimasukkan sebagai dukungan bawaan browser, hambatan masuk bagi browser baru akan makin tinggi, dan mustahil mendukung semua pustaka serta runtime yang diinginkan orang
Jika masing-masing dibawa sendiri dan mengandalkan cache, masih tersisa pertanyaan bagaimana menghindari masalah yang sebelumnya dialami pada caching pustaka JavaScript
Misalnya, shared library Go dapat memuat runtime dan bagian inti pustaka standar yang dipakai banyak program
Bahkan tanpa dukungan dynamic library di dalam aplikasi, ukuran semua program Go dapat diperkecil, dan runtime bahasa tidak perlu melakukan optimisasi ruang secara ekstrem
Karena ia sudah dimuat, dan jika satu program saja memakai satu fungsi, itu bukan ruang yang terbuang
Ini mengubah model biaya optimisasi ukuran untuk program dalam bahasa tersebut
Fungsi pustaka standar yang disertakan pada dasarnya menjadi gratis begitu Anda memakai bahasanya, jadi tinggal digunakan saja
Namun masalah yang sama berulang pada pustaka dan framework yang umum dipakai
Saat berjalan di Cloudflare, orang juga akan ingin berbagi pustaka standar Cloudflare untuk Go
Masalahnya adalah bahasa tidak berevolusi dengan kecepatan yang sama seperti runtime
Dukungan untuk beberapa versi bahasa bisa jadi terbatas, atau shared library dapat menumpuk seiring waktu sehingga efek berbagi antar-aplikasi berkurang
JavaScript memiliki model versi berupa “tidak ada pilihan”, yang menuntut kompatibilitas mundur yang kuat dan sesekali polyfill
Ini mungkin kurang cocok untuk bahasa lain
Jika runtime benar-benar ingin mengurangi ruang, ia bisa membatasi keragaman plugin
Meski ada keluhan, pendekatan “harus memakai JavaScript” bekerja cukup baik di browser
Bahasa berbasis WebAssembly mungkin tidak perlu terlalu beragam, dan seperti situasi Menara Babel, keragaman memiliki biaya
Jika tree shaking dilakukan berdasarkan informasi produksi nyata, sepertinya banyak kode mati atau hampir mati bisa dipangkas tanpa harus mengimplementasikan algoritme analisis statis yang rumit
Terutama dalam konteks seperti Hoot, hal-hal seperti
appendChildadalah fungsi eksternal yang dipanggil dari dalam Scheme: https://spritely.institute/news/building-interactive-web-pages-with-guile-hoot.htmlSecara teori, di lingkungan Wasm apa pun, banyak bagian dari pustaka standar JavaScript dapat digunakan dengan cara seperti ini
Zig sangat cocok untuk penggunaan seperti ini
Secara pribadi, saya menganggap ukuran file Wasm di bawah 100KB bukan faktor penting, sedangkan jika melewati MB barulah penting
GC bawaan penting untuk sebagian aplikasi, tetapi tidak untuk semuanya, dan aplikasi web paling baik dibuat tanpa GC
Faktor terpenting agar aplikasi yang memakai Wasm berhasil tetaplah keuntungan performa
Saya menjalankan aplikasi Blazor di Cloudflare Pages; unduhannya cepat dan performanya bagus, tetapi waktu muatnya mengerikan
Saya rasa ini tidak bisa diselesaikan dengan .NET, dan masalah intinya tampaknya bahasa berorientasi objek memang dirancang sehingga semuanya saling terkait
Selain itu, sulit bersaing dengan skala dana yang digelontorkan ke JavaScript, dan di JavaScript copy/paste terasa seperti fitur bahasa, jadi seperti cheat code
Ketiga, bahkan di Blazor tetap masih dibutuhkan JavaScript dan keahlian di ekosistem itu, dan menurut saya itulah masalah utamanya
Masalahnya adalah bahasa-bahasa pada era itu bergantung pada refleksi untuk banyak use case
Orang-orang sedang bekerja keras untuk menghilangkan use case seperti ini dari sebagian besar .NET, dan menandainya sebagai aman untuk penghapusan kode yang tidak dipakai
Jika ada kemungkinan sebuah metode dipanggil lewat refleksi, sulit mengetahui apa yang bisa dihapus dengan aman
Meski tampaknya tidak ada yang memanggil
Foo.Bar(), bagaimana jika seseorang melakukanReflection.getClass(someClass).runMethod(someVar)dan variabel-variabel itu disetel ke"Foo"dan"Bar"Misalnya, Dart tidak mengizinkan refleksi pada aplikasi yang dikompilasi lebih dulu, sehingga kode yang tidak dipakai bisa dihapus dengan aman: https://docs.flutter.dev/resources/faq#does-flutter-come-with-a-reflection-mirrors-system
Dart adalah bahasa berorientasi objek, tetapi menghindari pembuatan kode saat runtime dan refleksi saat runtime, lalu memilih pembuatan kode saat compile time
.NET juga bergerak ke arah ini, tetapi itu bukan sesuatu yang selesai dalam semalam
Namun seperti yang dikatakan orang lain, bahasa non-JavaScript juga punya masalah bahwa bagian dari fungsi standard library yang ada di runtime JavaScript browser dan mereka gunakan harus mereka kirimkan sendiri
Model packaging saat ini tidak bisa benar-benar memanfaatkan kemampuan .NET trimming karena keterbatasan Wasm yang dipaketkan di atas Mono
Untuk melihat seberapa baik sebenarnya ia bisa diperkecil, lebih baik build aplikasi biasa dengan AOT; hasilnya akan berupa binary kecil
Dukungan eksperimental target NativeAOT-LLVM Wasm di
dotnet/runtimelabmemberikan ukuran bundle yang jauh lebih kecil dan performa yang jauh lebih baik, tetapi karena masih berada di bawahdotnet/runtimelab, bukandotnet/runtime, saya tidak tahu kapan itu akan bisa digunakan