2 poin oleh GN⁺ 2024-04-15 | 1 komentar | Bagikan ke WhatsApp
  • 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 display memperluas cakupan kode yang diperlukan secara besar
    • Saat memanggil display untuk mencetak string, seluruh fasilitas I/O buffered ikut terbawa
    • Karena display dapat mencetak apa saja, kode untuk berbagai kasus seperti bitvector dan pair juga bisa ikut tertarik
    • Jika memanggil write-string yang hanya menggunakan string, kode output data umum dapat dihindari, tetapi fasilitas I/O buffered umum seperti port tetap disertakan

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 display bisa menjadi kode mati
  • Untuk mengetahui ini, perlu diketahui jenis argumen apa yang digunakan saat display dipanggil, dan untuk itu diperlukan analisis alur tingkat tinggi
  • Di Python, masalahnya menjadi lebih sulit
    • Dispatch berorientasi objek adalah pemrograman tingkat tinggi, sehingga arti foo.bar bergantung pada apa itu foo
    • 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

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

 
GN⁺ 2024-04-15
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 dependensi
    Saat ini hanya memakai rand dan fxhash, tetapi rand tampaknya juga bisa dihapus; fxhash hanya digunakan sebagai hash status game untuk memeriksa apakah terjadi desinkronisasi
    Jenis instans generik juga dikurangi; karena Vec sudah ada, tipe seperti Box<[i16]> tidak ikut ditarik masuk, dan menghilangkan floating-point serta hashmap juga membantu mengurangi keragaman tipe
    Algoritmenya 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

    • Menarik bahwa aritmetika fixed-point menghemat cukup banyak ruang, meskipun Wasm punya tipe floating-point bawaan
      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
    • Hal lain yang membantu adalah memakai wasm-opt dari binaryen
      Tampaknya 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
    • Saya membuat library deep reinforcement learning C++ RLtools dan juga contoh Wasm (https://rl.tools); meskipun sama sekali tidak memperhatikan ukuran biner, totalnya sekitar 200–300KB
      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 kira JavaScript memakai double untuk semua angka, jadi saya penasaran apakah Wasm benar-benar berbeda dalam hal ini
      Saya ingin memahami bagaimana pernyataan bahwa menghindari floating-point dan memakai aritmetika fixed-point dapat menghemat ruang bisa masuk akal
    • Saya ingin melihat angka konkret tentang seberapa banyak ruang yang benar-benar dihemat
      Khususnya, cara memakai Vec seperti Box seperti pada poin 6 sepertinya tidak akan menghasilkan penghematan besar
  • Nama 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 main tanpa sistem runtime pun dapat dibuat dengan baik, dan sistem runtime hanya diperlukan untuk stack trace dan garbage collection sehingga bisa dihilangkan jika diinginkan

    • Treeshaker sebagai nama alat deployment aplikasi kemungkinan berasal dari Lisp
      Contoh 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
    • Istilah yang umumnya tepat adalah dead-code elimination: https://en.wikipedia.org/wiki/Dead-code_elimination
      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
    • Ada banyak nama yang keliru dalam perangkat lunak, tetapi secara pribadi saya menganggap tree shaking nama yang bagus
      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
    • Saya tidak pernah mengira tree shaking merujuk pada pohon sebagai tanaman, melainkan terpikir tree sebagai struktur data
      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
    • Ada banyak kode, dan itu berbentuk tree
      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

    • Saya penasaran apakah WasmGC juga akan membantu 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 100KB
      Membandingkan 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

    • Penting untuk menyertakan syarat sebelumnya, yaitu “jika bersama GC
      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
    • Baru-baru ini saya mencoba Leptos untuk Rust; ini framework yang sangat cepat untuk membuat frontend web dengan Wasm, dan jujur terlihat cukup bagus
      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-opt dan kompresi Brotli, bundle Wasm untuk aplikasi counter ini berukuran 37KB; masih di kisaran yang mirip React, tetapi jauh lebih cepat setelah dijalankan
      Saya belum mencoba manipulasi DOM langsung, tetapi untuk komponen umum terlihat bagus
    • Sejauh yang saya pahami, salah satu tujuan WASI adalah menyediakan API yang langsung menuju DOM
      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

    • “Dead code elimination” biasanya merujuk pada optimisasi compiler berskala kecil yang membuang potongan kode di dalam fungsi yang tidak akan pernah tercapai
      “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
    • Apakah suatu istilah bertahan dan menjadi arus utama tidak terlalu berkaitan dengan seberapa lama istilah itu ada
      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
    • Jika memang berasal dari konteks Lisp, mungkin karena penghapusan data dan metadata yang tidak diperlukan, bukan hanya kode yang dieksekusi, diperlakukan dengan bobot yang sama
    • Ungkapan dead code elimination jauh lebih baik
    • Ungkapan itu lebih hidup dan terdengar lebih percakapan daripada “dead code elimination”, jadi tidak sulit memahami mengapa menyebar
  • 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

    • Biasanya yang dipanen dengan diguncang adalah kacang-kacangan
      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

    • Deteksi tabrakan per piksel terdengar bagus saat pertama kali didengar, tetapi saat benar-benar dimainkan bisa terasa tidak menyenangkan
      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
    • Jika 256 tabrakan menghasilkan 1fps, bottleneck-nya adalah algoritma, bukan bahasa pemrogramannya
      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

    • Gagasan bahwa browser melakukan preload beberapa runtime dan pustaka populer agar dibagikan oleh halaman web terdengar mirip dengan ide lama: jika framework JavaScript umum disediakan dari CDN bersama, sebagian besar browser sudah memilikinya di cache sehingga tidak perlu diunduh, dan ukuran framework tidak terlalu penting
      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
    • Bahkan dukungan dynamic linking yang terbatas tampaknya bisa memberi dampak cukup besar
      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
    • Saya penasaran apakah mungkin membuat modul kecil yang hanya berisi kode yang dibutuhkan aplikasi melalui tree shaking berbasis profil, lalu mengambil fitur yang jarang dipakai melalui dynamic linking saat diperlukan
      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
    • Struktur di mana browser mengambil runtime populer terlebih dahulu pada akhirnya berisiko membuat ratusan versi runtime terunduh ke browser, masing-masing biner hanya dipakai oleh satu situs, lalu memenuhi cache
    • Secara umum saya setuju, tetapi saya ingin menekankan bahwa di Wasm pun, jika ingin melakukan sesuatu selain komputasi murni, Anda akan cukup banyak memakai JavaScript bawaan browser
      Terutama dalam konteks seperti Hoot, hal-hal seperti appendChild adalah fungsi eksternal yang dipanggil dari dalam Scheme: https://spritely.institute/news/building-interactive-web-pages-with-guile-hoot.html
      Secara 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

    • Itu bukan masalah berorientasi objek
      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 melakukan Reflection.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
    • Blazor tidak unggul dalam aspek ini
      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/runtimelab memberikan ukuran bundle yang jauh lebih kecil dan performa yang jauh lebih baik, tetapi karena masih berada di bawah dotnet/runtimelab, bukan dotnet/runtime, saya tidak tahu kapan itu akan bisa digunakan