Pengumpulan Sampah WebAssembly (WasmGC) kini diaktifkan secara default di Chrome

Ada dua jenis bahasa pemrograman: bahasa pemrograman yang dibersihkan dari tumpukan sampah dan bahasa pemrograman yang memerlukan manajemen memori manual. Contoh yang pertama, di antaranya adalah Kotlin, PHP, atau Java. Contoh yang terakhir adalah C, C++, atau Rust. Sebagai aturan umum, bahasa pemrograman tingkat tinggi lebih cenderung memiliki pembersihan sampah memori sebagai fitur standar. Dalam postingan blog ini, fokusnya adalah pada bahasa pemrograman yang dibersihkan dari sampah memori dan bagaimana bahasa tersebut dapat dikompilasi ke WebAssembly (Wasm). Namun, untuk memulai pembersihan sampah memori (sering disebut sebagai GC) dari apa?

Dukungan Browser

  • 119
  • 119
  • 120
  • x

Pembersihan sampah memori

Dalam istilah sederhana, pembersihan sampah memori adalah upaya untuk mendapatkan kembali memori yang dialokasikan oleh program, tetapi tidak lagi direferensikan. Memori semacam ini disebut sampah. Ada banyak strategi untuk menerapkan pembersihan sampah memori. Salah satunya adalah penghitungan referensi yang tujuannya adalah menghitung jumlah referensi ke objek dalam memori. Jika tidak ada lagi referensi ke sebuah objek, objek tersebut dapat ditandai sebagai tidak lagi digunakan sehingga siap untuk pembersihan sampah memori. Pembersih sampah memori PHP menggunakan penghitungan referensi, dan penggunaan fungsi xdebug_debug_zval() dari ekstensi Xdebug memungkinkan Anda untuk mengintip dari balik layar. Pertimbangkan program PHP berikut.

<?php
  $a= (string) rand();
  $c = $b = $a;
  $b = 42;
  unset($c);
  $a = null;
?>

Program ini menetapkan angka acak yang ditransmisikan ke string ke variabel baru yang disebut a. Kemudian, tindakan ini membuat dua variabel baru, b dan c, dan menetapkan nilai a kepada keduanya. Setelah itu, b akan ditetapkan ulang ke nomor 42, lalu membatalkan setelan c. Terakhir, kode ini menetapkan nilai a ke null. Menganotasi setiap langkah program dengan xdebug_debug_zval(), Anda dapat melihat penghitung referensi pembersih sampah memori berfungsi.

<?php
  $a= (string) rand();
  $c = $b = $a;
  xdebug_debug_zval('a');
  $b = 42;
  xdebug_debug_zval('a');
  unset($c);
  xdebug_debug_zval('a');
  $a = null;
  xdebug_debug_zval('a');
?>

Contoh di atas akan menghasilkan log berikut, tempat Anda melihat bagaimana jumlah referensi ke nilai variabel a berkurang setelah setiap langkah, yang masuk akal mengingat urutan kode. (Angka acak Anda tentu saja akan berbeda.)

a:
(refcount=3, is_ref=0)string '419796578' (length=9)
a:
(refcount=2, is_ref=0)string '419796578' (length=9)
a:
(refcount=1, is_ref=0)string '419796578' (length=9)
a:
(refcount=0, is_ref=0)null

Ada tantangan lain terkait pembersihan sampah memori, seperti mendeteksi siklus, tetapi untuk artikel ini, memiliki pemahaman dasar tentang penghitungan referensi sudah cukup.

Bahasa pemrograman diimplementasikan dalam bahasa pemrograman lain

Mungkin terasa seperti awal, tetapi bahasa pemrograman diterapkan dalam bahasa pemrograman lain. Misalnya, runtime PHP utamanya diimplementasikan di C. Anda dapat melihat kode sumber PHP di GitHub. Kode pembersihan sampah memori PHP sebagian besar terletak dalam file zend_gc.c. Sebagian besar developer akan menginstal PHP melalui pengelola paket sistem operasi mereka. Namun, developer juga dapat membuat PHP dari kode sumbernya. Misalnya, di lingkungan Linux, langkah-langkah ./buildconf && ./configure && make akan membuat PHP untuk runtime Linux. Namun, ini juga berarti bahwa runtime PHP dapat dikompilasi untuk runtime lain, seperti Wasm.

Metode tradisional untuk mentransfer bahasa ke runtime Wasm

Secara terpisah dari platform tempat PHP berjalan, skrip PHP dikompilasi ke dalam bytecode yang sama dan dijalankan oleh Zend Engine. Zend Engine adalah lingkungan compiler dan runtime untuk bahasa skrip PHP. Platform ini terdiri dari Zend Virtual Machine (VM), yang terdiri dari Zend Compiler dan Zend Executor. Bahasa seperti PHP yang diimplementasikan dalam bahasa tingkat tinggi lainnya seperti C biasanya memiliki pengoptimalan yang menargetkan arsitektur tertentu, seperti Intel atau ARM, dan memerlukan backend yang berbeda untuk setiap arsitektur. Dalam konteks ini, Wasm mewakili arsitektur baru. Jika VM memiliki kode khusus arsitektur, seperti kompilasi just-in-time (JIT) atau kompilasi ahead-of-time (AOT), developer juga akan mengimplementasikan backend untuk JIT/AOT untuk arsitektur yang baru. Pendekatan ini sangat masuk akal karena sering kali bagian utama codebase dapat dikompilasi ulang untuk setiap arsitektur baru.

Mengingat rendahnya tingkat Wasm, sebaiknya coba pendekatan yang sama: Kompilasi ulang kode VM utama dengan parser, dukungan library, pembersihan sampah memori, dan pengoptimal ke Wasm, dan terapkan backend JIT atau AOT untuk Wasm jika diperlukan. Hal ini dimungkinkan sejak Wasm MVP, dan berfungsi dengan baik dalam praktiknya di banyak kasus. Bahkan, PHP yang dikompilasi ke Wasm adalah yang mendukung WordPress Playground. Pelajari project ini lebih lanjut di artikel Membuat pengalaman WordPress dalam browser dengan WordPress Playground dan WebAssembly.

Namun, PHP Wasm berjalan di browser dalam konteks JavaScript bahasa host. Di Chrome, JavaScript dan Wasm dijalankan di V8, yaitu mesin JavaScript open source Google yang menerapkan ECMAScript seperti yang ditentukan dalam ECMA-262. Selain itu, V8 sudah memiliki pembersih sampah memori. Artinya, developer yang menggunakan, misalnya, PHP yang dikompilasi ke Wasm, pada akhirnya mengirimkan implementasi bahasa yang ditransfer (PHP) oleh pembersih sampah memori ke browser yang sudah memiliki pembersih sampah memori, dan hal ini sama borosnya kedengarannya. Di sinilah WasmGC berperan.

Masalah lain dari pendekatan lama yang membiarkan modul Wasm membangun GC mereka sendiri di atas memori linear Wasm adalah bahwa tidak ada interaksi antara pembersih sampah Wasm dan pembersih sampah memori bawaan dari bahasa yang dikompilasi ke Wasm, yang cenderung menyebabkan masalah seperti kebocoran memori dan upaya pengumpulan yang tidak efisien. Membiarkan modul Wasm menggunakan kembali GC bawaan yang ada akan menghindari masalah ini.

Melakukan porting bahasa pemrograman ke runtime baru dengan WasmGC

WasmGC adalah proposal dari Grup Komunitas WebAssembly. Implementasi Wasm MVP saat ini hanya mampu menangani angka, yaitu bilangan bulat dan float, dalam memori linear, dan dengan proposal jenis referensi yang dikirimkan, Wasm juga dapat mempertahankan referensi eksternal. WasmGC sekarang menambahkan jenis heap struct dan array, yang berarti dukungan untuk alokasi memori non-linear. Setiap objek WasmGC memiliki jenis dan struktur tetap, yang memudahkan VM untuk membuat kode yang efisien untuk mengakses kolomnya tanpa risiko depengoptimalan yang dimiliki bahasa dinamis seperti JavaScript. Dengan demikian, proposal ini menambahkan dukungan yang efisien untuk bahasa terkelola tingkat tinggi ke WebAssembly, melalui jenis heap struct dan array yang memungkinkan compiler bahasa yang menargetkan Wasm berintegrasi dengan pembersih sampah memori di VM host. Dalam istilah yang disederhanakan, ini berarti bahwa dengan WasmGC, mem-porting bahasa pemrograman ke Wasm berarti pembersih sampah memori bahasa pemrograman tidak perlu lagi menjadi bagian dari port, tetapi sebagai gantinya pembersih sampah memori yang ada dapat digunakan.

Untuk memverifikasi dampak nyata dari peningkatan ini, tim Wasm Chrome telah mengompilasi versi tolok ukur Fannkuch (yang mengalokasikan struktur data saat berfungsi) dari C, Rust, dan Java. Biner C dan Rust dapat berkisar dari 6,1 K hingga 9,6 K bergantung pada berbagai flag compiler, sedangkan versi Java jauh lebih kecil yaitu hanya 2,3 K. C dan Rust tidak menyertakan pembersih sampah memori, tetapi mereka tetap memaketkan malloc/free untuk mengelola memori, dan alasan Java di sini lebih kecil karena Java tidak perlu memaketkan kode pengelolaan memori apa pun. Ini hanyalah satu contoh spesifik, tetapi ini menunjukkan bahwa biner WasmGC memiliki potensi sangat kecil, dan ini bahkan sebelum ada upaya signifikan untuk mengoptimalkan ukuran.

Melihat cara kerja bahasa pemrograman yang ditransfer WasmGC

Wasm Kotlin

Salah satu bahasa pemrograman pertama yang telah ditransfer ke Wasm berkat WasmGC adalah Kotlin dalam bentuk Kotlin/Wasm. Demo, dengan kode sumber milik tim Kotlin, ditampilkan dalam listingan berikut.

import kotlinx.browser.document
import kotlinx.dom.appendText
import org.w3c.dom.HTMLDivElement

fun main() {
    (document.getElementById("warning") as HTMLDivElement).style.display = "none"
    document.body?.appendText("Hello, ${greet()}!")
}

fun greet() = "world"

Sekarang Anda mungkin bertanya-tanya apa intinya, karena kode Kotlin di atas pada dasarnya terdiri dari JavaScript OM API yang dikonversi ke Kotlin. Ini mulai lebih masuk akal jika dikombinasikan dengan Compose Multiplatform, yang memungkinkan developer mem-build berdasarkan UI yang mungkin sudah mereka buat untuk aplikasi Android Kotlin. Lihat eksplorasi awal hal ini dengan demo penampil gambar Kotlin/Wasm dan jelajahi kode sumbernya, juga oleh tim Kotlin.

Dart dan Flutter

Tim Dart dan Flutter di Google juga sedang menyiapkan dukungan untuk WasmGC. Pekerjaan kompilasi Dart-to-Wasm hampir selesai, dan tim sedang mengerjakan dukungan alat untuk mengirimkan aplikasi web Flutter yang dikompilasi ke WebAssembly. Anda dapat membaca status pekerjaan saat ini di dokumentasi Flutter. Demo berikut adalah Pratinjau Flutter WasmGC.

Pelajari WasmGC lebih lanjut

Postingan blog ini hampir tidak muncul dan sebagian besar memberikan ringkasan umum tentang WasmGC. Untuk mempelajari fitur ini lebih lanjut, buka link berikut:

Ucapan terima kasih

Banner besar oleh Gary Chan di Unsplash. Artikel ini ditinjau oleh Matthias Liedtke, Adam Klein, Joshua Bell, Alon Zakai, Jakob Kummerow, Clemens Backes, Emanuel Ziegler, dan Rachel Andrew.