WebAssembly Çöp Toplama (WasmGC) artık Chrome'da varsayılan olarak etkin

İki tür programlama dili vardır: çöp toplayıcı programlama dilleri ve manuel bellek yönetimi gerektiren programlama dilleri. Kotlin, PHP veya Java, bu tür programlama dillerine örnek olarak verilebilir. C, C++ veya Rust, bu tür programlama dillerine örnek olarak verilebilir. Genel bir kural olarak, üst düzey programlama dillerinde standart bir özellik olarak çöp toplama özelliğinin bulunması daha olasıdır. Bu blog yayınında, bu tür çöp toplayıcı programlama dilleri ve bunların WebAssembly'e (Wasm) nasıl derlenebileceği ele alınmaktadır. Peki, öncelikle, çöp toplama (genellikle GC olarak adlandırılır) nedir?

Tarayıcı desteği

  • Chrome: 119.
  • Edge: 119.
  • Firefox: 120.
  • Safari: Desteklenmez.

Atık toplama

Basitleştirilmiş bir ifadeyle, program tarafından ayrılmış ancak artık referans verilmeyen belleği geri alma girişimi, çöp toplama fikridir. Bu tür hafızalara "çöp" denir. Çöp toplama işlemini uygulamak için birçok strateji vardır. Bunlardan biri, amaç bellekteki nesnelere yapılan referansların sayısını saymak olan referans sayımı. Bir nesneye artık referans verilmediğinde, artık kullanılmadığı ve çöp toplama işlemine hazır olduğu işaretlenebilir. PHP'nin çöp toplayıcısı referans sayımı kullanır. Xdebug uzantısının xdebug_debug_zval() işlevini kullanarak çöp toplayıcının işleyişine göz atabilirsiniz. Aşağıdaki PHP programını ele alalım.

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

Program, bir dizeye yayınlanan rastgele bir sayıyı a adlı yeni bir değişkene atar. Ardından b ve c adlı iki yeni değişken oluşturur ve bunlara a değerini atar. Ardından b, 42 numarasına yeniden atanır ve c'nin değeri kaldırılır. Son olarak a değerini null olarak ayarlar. Programın her adımını xdebug_debug_zval() ile notlandırarak çöp toplayıcının referans sayacını çalışırken görebilirsiniz.

<?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');
?>

Yukarıdaki örnekte aşağıdaki günlükler oluşturulur. Bu günlüklerde, a değişkeninin değerine yapılan referansların her adımdan sonra nasıl azaldığını görürsünüz. Bu durum, kod sırası göz önüne alındığında mantıklı bir durumdur. (Elbette rastgele sayınız farklı olacaktır.)

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

Çöp toplamayla ilgili döngüleri algılama gibi başka zorluklar da vardır ancak bu makale için referans sayımını temel düzeyde anlamak yeterlidir.

Programlama dilleri diğer programlama dillerinde uygulanır

Bu durum başlangıç gibi görünse de programlama dilleri diğer programlama dillerinde uygulanır. Örneğin, PHP çalışma zamanı esas olarak C'de uygulanır. GitHub'daki PHP kaynak koduna göz atabilirsiniz. PHP'nin atık toplama kodu çoğunlukla zend_gc.c dosyasında bulunur. Çoğu geliştirici, PHP'yi işletim sisteminin paket yöneticisi aracılığıyla yükler. Ancak geliştiriciler PHP'yi kaynak kodundan da derleyebilirler. Örneğin, Linux ortamında ./buildconf && ./configure && make adımları, Linux çalışma zamanı için PHP'yi derleyecektir. Ancak bu, PHP çalışma zamanının Wasm gibi diğer çalışma zamanları için derlenebileceği anlamına da gelir.

Dilleri Wasm çalışma zamanına taşımanın geleneksel yöntemleri

PHP'nin çalıştığı platformdan bağımsız olarak PHP komut dosyaları aynı bayt koduna derlenir ve Zend Engine tarafından çalıştırılır. Zend Engine, PHP komut dosyası dili için bir derleyici ve çalışma zamanı ortamıdır. Zend Derleyici ve Zend Yürütücü'den oluşan Zend Sanal Makinesi'nden (VM) oluşur. C gibi diğer yüksek düzey dillerde uygulanan PHP gibi diller genellikle Intel veya ARM gibi belirli mimarileri hedefleyen optimizasyonlara sahiptir ve her mimari için farklı bir arka uç gerektirir. Bu bağlamda Wasm yeni bir mimariyi temsil eder. Sanal makinede tam zamanında (JIT) veya önceden derleme (AOT) gibi mimariye özgü kod varsa geliştirici, yeni mimari için JIT/AOT arka ucu da uygular. Kod tabanının ana kısmı genellikle her yeni mimari için yeniden derlenebileceğinden bu yaklaşım çok mantıklı bir yaklaşımdır.

Wasm'in düşük seviyeli olması nedeniyle burada da aynı yaklaşımı denemek doğaldır: Ana sanal makine kodunu, ayrıştırıcısı, kütüphane desteği, çöp toplama ve optimize edicisiyle birlikte Wasm olarak yeniden derleyin ve gerekirse Wasm için JIT veya AOT arka ucu uygulayın. Bu, Wasm MVP'den beri mümkün olmuştur ve birçok durumda pratikte iyi sonuç verir. WordPress Playground'u destekleyen şey aslında Wasm için derlenmiş PHP'dir. WordPress Playground ve WebAssembly ile tarayıcı içi WordPress deneyimleri oluşturma makalesinde proje hakkında daha fazla bilgi edinebilirsiniz.

Ancak PHP Wasm, tarayıcısında ana dil JavaScript bağlamında çalışır. Chrome'da JavaScript ve Wasm, Google'ın ECMAScript'i ECMA-262'da belirtildiği şekilde uygulayan açık kaynak JavaScript motoru V8'de çalıştırılır. Ayrıca V8'de zaten bir çöp toplayıcı var. Bu, örneğin Wasm'e derlenmiş PHP kullanan geliştiricilerin, aktarılan dilin (PHP) bir çöp toplayıcı uygulamasını, zaten çöp toplayıcısı olan tarayıcıya gönderdiği anlamına gelir. Bu da kulağa geldiği kadar israftır. İşte bu noktada WasmGC devreye girer.

Wasm modüllerinin Wasm'in doğrusal belleği üzerine kendi GC'lerini oluşturmasına izin verme şeklindeki eski yaklaşımın diğer sorunu, Wasm'in kendi çöp toplayıcısı ile Wasm'e derlenmiş dilin üzerine inşa edilmiş çöp toplayıcısı arasında hiçbir etkileşim olmamasıdır. Bu durum, bellek sızıntıları ve verimsiz toplama girişimleri gibi sorunlara neden olur. Wasm modüllerinin mevcut yerleşik GC'yi yeniden kullanmasına izin vermek bu sorunları önler.

WasmGC ile programlama dillerini yeni çalışma zamanlarına taşıma

WasmGC, WebAssembly Topluluk Grubu'nun bir önerisidir. Mevcut Wasm MVP uygulaması, doğrusal bellekte yalnızca sayılarla (yani tam sayılar ve kayan noktalı sayılar) işlem yapabilir. Referans türleri önerisi kullanıma sunulduğunda Wasm, harici referansları da tutabilir. WasmGC artık yapı ve dizi yığın türleri ekliyor. Bu da doğrusal olmayan bellek ayırma desteği anlamına geliyor. Her WasmGC nesnesi sabit bir türe ve yapıya sahiptir. Bu, VM'lerin JavaScript gibi dinamik dillerin sahip olduğu optimizasyondan çıkarma riski olmadan alanlarına erişmek için verimli kod oluşturmasını kolaylaştırır. Bu öneri, Wasm'i hedefleyen dil derleyicilerinin ana makine sanal makinesindeki bir çöp toplayıcıyla entegrasyonunu sağlayan yapı ve dizi yığın türleri aracılığıyla WebAssembly'e yüksek düzey yönetilen diller için etkili bir destek ekler. Basitçe ifade etmek gerekirse, WasmGC ile bir programlama dilini Wasm'e taşımak, programlama dilinin artık bağlantı noktasının bir parçası olması gerekmediği anlamına gelir. Bunun yerine mevcut çöp toplayıcı kullanılabilir.

Bu iyileştirmenin gerçek dünyadaki etkisini doğrulamak için Chrome'un Wasm ekibi, Fannkuch karşılaştırma testi'nin (çalışırken veri yapılarını ayıran) C, Rust ve Java sürümlerini derledi. C ve Rust ikilileri, çeşitli derleyici işaretlerine bağlı olarak 6,1 K ile 9,6 K arasında değişebilir. Java sürümü ise yalnızca 2,3 K ile çok daha küçüktür. C ve Rust'ta çöp toplayıcı bulunmasa da belleği yönetmek için malloc/free paketlenir. Java'nın burada daha küçük olmasının nedeni, bellek yönetimi kodunu paketlemesi gerekmemesidir. Bu yalnızca belirli bir örnektir ancak WasmGC ikili dosyalarının çok küçük olma potansiyeline sahip olduğunu gösterir. Bu durum, boyut optimizasyonu için önemli bir çalışma yapılmadan önce bile geçerlidir.

WasmGC'ye taşınmış bir programlama dilini çalışırken görme

Kotlin Wasm

WasmGC sayesinde Wasm'e taşınan ilk programlama dillerinden biri, Kotlin/Wasm biçiminde Kotlin'dir. Kotlin ekibinin kaynak kodunu içeren demo aşağıdaki girişte gösterilmektedir.

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"

Yukarıdaki Kotlin kodu temel olarak Kotlin'e dönüştürülmüş JavaScript OM API'lerinden oluştuğu için bunun ne anlama geldiğini merak ediyor olabilirsiniz. Compose Multiplatform ile birlikte kullanıldığında bu yaklaşım daha anlamlı hale gelir. Compose Multiplatform, geliştiricilerin Android Kotlin uygulamaları için oluşturdukları kullanıcı arayüzünü temel alarak geliştirme yapmalarına olanak tanır. Kotlin ekibinin sunduğu Kotlin/Wasm görüntü görüntüleyici demosunu inceleyerek bu özelliğin erken dönem keşfine göz atabilir ve kaynak kodunu keşfedebilirsiniz.

Dart ve Flutter

Google'daki Dart ve Flutter ekipleri de WasmGC desteğini hazırlıyor. Dart'tan Wasm'e derleme çalışması neredeyse tamamlandı. Ekip, WebAssembly'e derlenmiş Flutter web uygulamalarını yayınlamak için araç desteği üzerinde çalışıyor. Çalışmanın mevcut durumu hakkında bilgi edinmek için Flutter dokümanlarını inceleyebilirsiniz. Aşağıdaki demo, Flutter WasmGC Önizlemesi'dir.

WasmGC hakkında daha fazla bilgi edinin

Bu blog yayınında, WasmGC'ye genel bir bakış sunulmuştur. Bu özellik hakkında daha fazla bilgi edinmek için şu bağlantılara göz atın:

Teşekkür ederiz

Bu makale; Matthias Liedtke, Adam Klein, Joshua Bell, Alon Zakai, Jakob Kummerow, Clemens Backes, Emanuel Ziegler ve Rachel Andrew tarafından incelendi.