WebAssembly गार्बेज कलेक्शन (WasmGC) को अब Chrome में डिफ़ॉल्ट रूप से चालू कर दिया गया है

प्रोग्रामिंग लैंग्वेज दो तरह की होती हैं: गार्बेज-कलेक्टेड प्रोग्रामिंग लैंग्वेज और ऐसी प्रोग्रामिंग लैंग्वेज जिनमें मेमोरी को मैन्युअल तरीके से मैनेज करना पड़ता है. Kotlin, PHP या Java, पहले वाले उदाहरणों में से कुछ हैं. बाद वाले के उदाहरण C, C++ या Rust हैं. आम तौर पर, ज़्यादातर प्रोग्रामिंग भाषाओं में गार्बेज कलेक्शन की सुविधा स्टैंडर्ड तौर पर उपलब्ध होती है. इस ब्लॉग पोस्ट में, ऐसी प्रोग्रामिंग भाषाओं पर फ़ोकस किया गया है जिनमें गार्बेज कलेक्शन की सुविधा होती है. साथ ही, यह भी बताया गया है कि उन्हें WebAssembly (Wasm) में कैसे कंपाइल किया जा सकता है. हालांकि, गार्बेज कलेक्शन (जिसे अक्सर GC कहा जाता है) क्या है?

Browser Support

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

गार्बेज कलेक्शन

आसान शब्दों में कहें, तो गार्बेज कलेक्शन का मतलब है कि प्रोग्राम की ओर से मेमोरी को वापस पाने की कोशिश की जाती है. हालांकि, अब इसका इस्तेमाल नहीं किया जा रहा है. इस तरह की मेमोरी को गार्बेज कहा जाता है. गारबेज कलेक्शन को लागू करने के लिए, कई रणनीतियां हैं. इनमें से एक रेफ़रंस काउंटिंग है. इसका मकसद मेमोरी में मौजूद ऑब्जेक्ट के रेफ़रंस की संख्या गिनना है. जब किसी ऑब्जेक्ट का कोई रेफ़रंस नहीं होता है, तो उसे 'अब इस्तेमाल नहीं किया जा रहा है' के तौर पर मार्क किया जा सकता है. इस तरह, वह ऑब्जेक्ट कचरा इकट्ठा करने के लिए तैयार हो जाता है. PHP का गार्बेज कलेक्टर रेफ़रंस काउंटिंग का इस्तेमाल करता है. साथ ही, Xdebug एक्सटेंशन के xdebug_debug_zval() फ़ंक्शन का इस्तेमाल करके, इसके बारे में ज़्यादा जानकारी पाई जा सकती है. यहां दिए गए PHP प्रोग्राम पर ध्यान दें.

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

यह प्रोग्राम, स्ट्रिंग में कास्ट किए गए किसी रैंडम नंबर को a नाम के नए वैरिएबल को असाइन करता है. इसके बाद, यह दो नए वैरिएबल, b और c बनाता है और उन्हें a की वैल्यू असाइन करता है. इसके बाद, यह b को 42 नंबर पर फिर से असाइन करता है. इसके बाद, c को अनसेट कर देता है. आखिर में, यह a की वैल्यू को null पर सेट करता है. प्रोग्राम के हर चरण को xdebug_debug_zval() से एनोटेट करके, गार्बेज कलेक्टर के रेफ़रंस काउंटर को काम करते हुए देखा जा सकता है.

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

ऊपर दिए गए उदाहरण से, ये लॉग आउटपुट होंगे. इनमें आपको दिखेगा कि हर चरण के बाद, वैरिएबल a की वैल्यू के रेफ़रंस की संख्या कैसे कम होती है. यह कोड के क्रम के हिसाब से सही है. (आपका रैंडम नंबर अलग होगा.)

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

कचरा इकट्ठा करने में अन्य समस्याएं भी आती हैं, जैसे कि साइकल का पता लगाना. हालांकि, इस लेख के लिए, रेफ़रंस काउंटिंग के बारे में बुनियादी जानकारी होना काफ़ी है.

प्रोग्रामिंग भाषाओं को अन्य प्रोग्रामिंग भाषाओं में लागू किया जाता है

यह सुनने में थोड़ा अजीब लग सकता है, लेकिन प्रोग्रामिंग भाषाओं को दूसरी प्रोग्रामिंग भाषाओं में लागू किया जाता है. उदाहरण के लिए, PHP रनटाइम को मुख्य रूप से C में लागू किया जाता है. GitHub पर PHP का सोर्स कोड देखा जा सकता है. PHP का गार्बेज कलेक्शन कोड, मुख्य रूप से zend_gc.c फ़ाइल में मौजूद होता है. ज़्यादातर डेवलपर, अपने ऑपरेटिंग सिस्टम के पैकेज मैनेजर के ज़रिए PHP इंस्टॉल करेंगे. हालांकि, डेवलपर सोर्स कोड से भी PHP बना सकते हैं. उदाहरण के लिए, Linux एनवायरमेंट में, ./buildconf && ./configure && make कमांड से Linux रनटाइम के लिए PHP बनाया जाएगा. हालांकि, इसका यह भी मतलब है कि PHP रनटाइम को अन्य रनटाइम के लिए कंपाइल किया जा सकता है. जैसे, Wasm.

Wasm रनटाइम में भाषाओं को पोर्ट करने के पारंपरिक तरीके

PHP स्क्रिप्ट को एक ही बाइटकोड में कंपाइल किया जाता है. साथ ही, इन्हें Zend Engine की मदद से चलाया जाता है. इससे कोई फ़र्क़ नहीं पड़ता कि PHP किस प्लैटफ़ॉर्म पर चल रहा है. Zend Engine, PHP स्क्रिप्टिंग लैंग्वेज के लिए कंपाइलर और रनटाइम एनवायरमेंट है. इसमें Zend Virtual Machine (VM) शामिल होता है. इसमें Zend Compiler और Zend Executor शामिल होते हैं. PHP जैसी लैंग्वेज, C जैसी अन्य हाई-लेवल लैंग्वेज में लागू की जाती हैं. इनमें आम तौर पर, Intel या ARM जैसे खास आर्किटेक्चर को टारगेट करने वाले ऑप्टिमाइज़ेशन होते हैं. साथ ही, हर आर्किटेक्चर के लिए अलग बैकएंड की ज़रूरत होती है. इस संदर्भ में, Wasm एक नए आर्किटेक्चर को दिखाता है. अगर वीएम में आर्किटेक्चर के हिसाब से कोड है, जैसे कि जस्ट-इन-टाइम (जेआईटी) या ऐड-ऑफ-टाइम (एओटी) कंपाइलेशन, तो डेवलपर नए आर्किटेक्चर के लिए जेआईटी/एओटी के लिए बैकएंड भी लागू करता है. यह तरीका काफ़ी काम का है, क्योंकि अक्सर कोडबेस के मुख्य हिस्से को हर नए आर्किटेक्चर के लिए फिर से कंपाइल किया जा सकता है.

Wasm के लो-लेवल होने की वजह से, इस पर भी उसी तरीके को आज़माया जा सकता है: मुख्य VM कोड को उसके पार्सर, लाइब्रेरी सपोर्ट, गार्बेज कलेक्शन, और ऑप्टिमाइज़र के साथ Wasm में फिर से कंपाइल करें. साथ ही, अगर ज़रूरत हो, तो Wasm के लिए JIT या AOT बैकएंड लागू करें. यह Wasm MVP के बाद से ही मुमकिन है. साथ ही, यह कई मामलों में अच्छी तरह से काम करता है. दरअसल, PHP को Wasm में कंपाइल किया जाता है. इसी से WordPress Playground काम करता है. इस प्रोजेक्ट के बारे में ज़्यादा जानने के लिए, WordPress Playground और WebAssembly की मदद से, ब्राउज़र में WordPress का अनुभव पाएं लेख पढ़ें.

हालांकि, PHP Wasm, ब्राउज़र में होस्ट लैंग्वेज JavaScript के कॉन्टेक्स्ट में चलता है. Chrome में, JavaScript और Wasm को V8 में चलाया जाता है. यह Google का ओपन सोर्स JavaScript इंजन है, जो ECMA-262 में बताए गए ECMAScript को लागू करता है. साथ ही, V8 में पहले से ही एक गार्बेज कलेक्टर मौजूद है. इसका मतलब है कि Wasm में कंपाइल की गई PHP का इस्तेमाल करने वाले डेवलपर, पोर्ट की गई भाषा (PHP) के गार्बेज कलेक्टर को ऐसे ब्राउज़र पर शिप करते हैं जिसमें पहले से ही गार्बेज कलेक्टर मौजूद होता है. यह उतना ही फ़िज़ूलखर्ची वाला है जितना लगता है. ऐसे में, WasmGC काम आता है.

Wasm मॉड्यूल को Wasm की लीनियर मेमोरी के ऊपर अपना GC बनाने की अनुमति देने के पुराने तरीके की एक और समस्या यह है कि Wasm के अपने गार्बेज कलेक्टर और Wasm में कंपाइल की गई भाषा के गार्बेज कलेक्टर के बीच कोई इंटरैक्शन नहीं होता है. इससे मेमोरी लीक और कलेक्शन के खराब तरीके जैसी समस्याएं होती हैं. Wasm मॉड्यूल को पहले से मौजूद GC का फिर से इस्तेमाल करने की अनुमति देने से, इन समस्याओं से बचा जा सकता है.

WasmGC की मदद से, प्रोग्रामिंग भाषाओं को नए रनटाइम पर पोर्ट करना

WasmGC, WebAssembly Community Group का एक प्रस्ताव है. फ़िलहाल, Wasm MVP सिर्फ़ लीनियर मेमोरी में मौजूद संख्याओं (यानी कि पूर्णांक और फ़्लोट) को प्रोसेस कर सकता है. साथ ही, रेफ़रंस टाइप के सुझाव को लागू करने के बाद, Wasm बाहरी रेफ़रंस को भी प्रोसेस कर सकता है. WasmGC में अब struct और array हीप टाइप जोड़े गए हैं. इसका मतलब है कि अब नॉन-लीनियर मेमोरी ऐलोकेशन की सुविधा उपलब्ध है. हर WasmGC ऑब्जेक्ट का टाइप और स्ट्रक्चर तय होता है. इससे वीएम के लिए, उनके फ़ील्ड को ऐक्सेस करने के लिए बेहतर कोड जनरेट करना आसान हो जाता है. साथ ही, डीऑप्टिमाइज़ेशन का जोखिम भी नहीं होता. यह जोखिम, JavaScript जैसी डाइनैमिक भाषाओं में होता है. इस प्रस्ताव के तहत, WebAssembly में मैनेज की गई भाषाओं के लिए बेहतर सपोर्ट जोड़ा गया है. इसके लिए, स्ट्रक्चर और ऐरे हीप टाइप का इस्तेमाल किया गया है. इससे Wasm को टारगेट करने वाले भाषा कंपाइलर, होस्ट वीएम में गार्बेज कलेक्टर के साथ इंटिग्रेट हो पाते हैं. आसान शब्दों में कहें, तो इसका मतलब यह है कि WasmGC की मदद से, किसी प्रोग्रामिंग लैंग्वेज को Wasm में पोर्ट करने का मतलब है कि प्रोग्रामिंग लैंग्वेज के गार्बेज कलेक्टर को अब पोर्ट का हिस्सा नहीं होना चाहिए. इसके बजाय, मौजूदा गार्बेज कलेक्टर का इस्तेमाल किया जा सकता है.

इस सुधार का असल दुनिया पर क्या असर पड़ा है, इसकी पुष्टि करने के लिए Chrome की Wasm टीम ने Fannkuch benchmark के वर्शन कंपाइल किए हैं. यह बेंचमार्क, काम करते समय डेटा स्ट्रक्चर असाइन करता है. ये वर्शन C, Rust, और Java से कंपाइल किए गए हैं. अलग-अलग कंपाइलर फ़्लैग के आधार पर, C और Rust बाइनरी का साइज़ 6.1 केबी से लेकर 9.6 केबी तक हो सकता है. वहीं, Java वर्शन का साइज़ सिर्फ़ 2.3 केबी है! C और Rust में गार्बेज कलेक्टर शामिल नहीं होता है. हालांकि, ये मेमोरी को मैनेज करने के लिए malloc/free को बंडल करते हैं. Java का साइज़ यहां इसलिए कम है, क्योंकि इसे मेमोरी मैनेजमेंट कोड को बंडल करने की ज़रूरत नहीं होती. यह सिर्फ़ एक उदाहरण है. इससे पता चलता है कि WasmGC बाइनरी का साइज़ बहुत छोटा हो सकता है. यह साइज़, साइज़ को ऑप्टिमाइज़ करने के लिए किए गए किसी भी अहम काम से पहले का है.

WasmGC पर पोर्ट की गई प्रोग्रामिंग लैंग्वेज को काम करते हुए देखना

Kotlin Wasm

WasmGC की मदद से, Wasm पर पोर्ट की गई पहली प्रोग्रामिंग लैंग्वेज में से एक Kotlin है. इसे Kotlin/Wasm के तौर पर पोर्ट किया गया है. यहां दी गई सूची में, Kotlin टीम की ओर से उपलब्ध कराया गया सोर्स कोड वाला डेमो दिखाया गया है.

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"

अब आपके मन में यह सवाल आ सकता है कि इसका क्या मतलब है, क्योंकि ऊपर दिए गए Kotlin कोड में, JavaScript OM API को Kotlin में बदला गया है. Compose Multiplatform के साथ इसका इस्तेमाल करने पर, यह ज़्यादा फ़ायदेमंद साबित होता है. इससे डेवलपर, अपने Android Kotlin ऐप्लिकेशन के लिए पहले से बनाए गए यूज़र इंटरफ़ेस (यूआई) को बेहतर बना सकते हैं. Kotlin/Wasm इमेज व्यूअर की मदद से, इस सुविधा को आज़माएं. इसे Kotlin टीम ने बनाया है.

Kotlin/Wasm इमेज व्यूअर का डेमो.

Dart और Flutter

Google में Dart और Flutter की टीमें भी WasmGC के लिए सहायता तैयार कर रही हैं. Dart-to-Wasm कंपाइलेशन का काम लगभग पूरा हो चुका है. साथ ही, टीम ऐसे टूल बनाने पर काम कर रही है जो WebAssembly में कंपाइल किए गए Flutter वेब ऐप्लिकेशन को डिलीवर करने में मदद करें. Flutter के दस्तावेज़ में जाकर, इस सुविधा के बारे में ज़्यादा जानें. यहां दिया गया डेमो, Flutter WasmGC Preview है.

WasmGC के बारे में ज़्यादा जानें

इस ब्लॉग पोस्ट में, WasmGC के बारे में सिर्फ़ बुनियादी जानकारी दी गई है. इसमें ज़्यादातर, WasmGC के बारे में हाई-लेवल की खास जानकारी दी गई है. इस सुविधा के बारे में ज़्यादा जानने के लिए, इन लिंक पर जाएं:

Acknowledgements

इस लेख की समीक्षा मथियास लीड्टके, ऐडम क्लेन, जोशुआ बेल, ऐलोन ज़काई, याकोब कुमेरोव, क्लेमेंस बैकेस, इमैनुअल ज़िगलर, और राशेल ऐंड्रयू ने की है.