เปิดใช้ WebAssembly Garbage Collection (WasmGC) ใน Chrome แล้วโดยค่าเริ่มต้น

ภาษาโปรแกรมมี 2 ประเภท ได้แก่ ภาษาโปรแกรมที่เก็บรวบรวมโดยขยะและภาษาโปรแกรมที่ต้องใช้การจัดการหน่วยความจำด้วยตนเอง เช่น Kotlin, PHP หรือ Java เป็นต้น ตัวอย่างกรณีหลังคือ C, C++ หรือ Rust ตามกฎทั่วไปแล้ว ภาษาโปรแกรมระดับสูงกว่ามีแนวโน้มที่จะมีการเก็บข้อมูลขยะเป็นฟีเจอร์มาตรฐาน ในบล็อกโพสต์นี้ เราให้ความสำคัญกับภาษาโปรแกรมที่เก็บรวบรวมเนื้อหาโดยที่ไม่มีเนื้อหาเหล่านี้ รวมทั้งวิธีการคอมไพล์ไปยัง WebAssembly (Wasm) แล้วถ้าจะเริ่มด้วยการเก็บขยะ (มักเรียกว่า GC) คืออะไร

การรองรับเบราว์เซอร์

  • Chrome: 119
  • ขอบ: 119
  • Firefox: 120
  • Safari: ไม่รองรับ

บริการเก็บขยะ

พูดง่ายๆ ก็คือแนวคิดเรื่องการจัดเก็บขยะคือความพยายามในการเรียกคืนหน่วยความจำที่โปรแกรมจัดสรรไว้ แต่ไม่ได้อ้างอิงถึงอีกต่อไป หน่วยความจำดังกล่าวเรียกว่าขยะ การใช้งานระบบเก็บขยะมีกลยุทธ์มากมาย หนึ่งในจำนวนดังกล่าวคือการนับการอ้างอิงซึ่งวัตถุประสงค์คือการนับจำนวนการอ้างอิงไปยังออบเจ็กต์ในหน่วยความจำ เมื่อไม่มีการอ้างอิงไปยังออบเจ็กต์แล้ว ก็ให้ทำเครื่องหมายว่าไม่ใช้แล้วได้ ซึ่งจะทำให้พร้อมสำหรับการเก็บขยะ เครื่องมือเก็บข้อมูลขยะของ PHP ใช้การนับการอ้างอิง และการใช้ฟังก์ชัน xdebug_debug_zval() ของส่วนขยาย Xdebug จะช่วยให้คุณแอบดูเบื้องหลังได้ ลองพิจารณาโปรแกรม PHP ต่อไปนี้

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

โปรแกรมจะกำหนดหมายเลขสุ่มที่แคสต์ไปยังสตริงให้กับตัวแปรใหม่ที่เรียกว่า a จากนั้นจะสร้างตัวแปรใหม่ 2 รายการ คือ 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 เป็นหลัก คุณดูซอร์สโค้ด PHP ใน GitHub ได้ รหัสการเก็บข้อมูลขยะของ PHP จะอยู่ในไฟล์ zend_gc.c เป็นหลัก นักพัฒนาซอฟต์แวร์ส่วนใหญ่จะติดตั้ง PHP ผ่านทางโปรแกรมจัดการแพ็กเกจของระบบปฏิบัติการ แต่นักพัฒนาซอฟต์แวร์ยังสามารถสร้าง PHP จากซอร์สโค้ด ตัวอย่างเช่น ในสภาพแวดล้อม Linux ขั้นตอนที่ ./buildconf && ./configure && make จะสร้าง PHP สำหรับรันไทม์ของ Linux แต่นั่นก็หมายความว่ารันไทม์ของ PHP สามารถคอมไพล์สำหรับรันไทม์อื่นๆ ได้ เหมือนอย่างที่ Wasm พูด

วิธีย้ายภาษาแบบดั้งเดิมไปยังรันไทม์ Wasm

โดยคอมไพล์สคริปต์ PHP คอมไพล์ในไบต์โค้ดเดียวกันและเรียกใช้โดย Zend Engine โดยทำงานแยกจากแพลตฟอร์มที่ PHP ทำงานอยู่ Zend Engine เป็นสภาพแวดล้อมคอมไพเลอร์และรันไทม์สำหรับภาษาสคริปต์ PHP ซึ่งประกอบด้วย Zend Virtual Machine (VM) ซึ่งประกอบด้วย Zend Compiler และ Zend Executor ภาษาอย่าง PHP ซึ่งใช้ในภาษาระดับสูงอื่นๆ เช่น C มักมีการเพิ่มประสิทธิภาพที่กำหนดเป้าหมายสถาปัตยกรรมเฉพาะ เช่น Intel หรือ ARM และจำเป็นต้องใช้แบ็กเอนด์ที่ต่างกันสำหรับแต่ละสถาปัตยกรรม ในบริบทนี้ Wasm จะแสดงสถาปัตยกรรมใหม่ หาก VM มีโค้ดเฉพาะสถาปัตยกรรม เช่น การคอมไพล์แบบ Just-In-Time (JIT) หรือการคอมไพล์ล่วงหน้า (AOT) นักพัฒนาซอฟต์แวร์จะใช้แบ็กเอนด์สำหรับ JIT/AOT สำหรับสถาปัตยกรรมใหม่ด้วย วิธีนี้เหมาะสมมากเนื่องจากมักมีการคอมไพล์ส่วนหลักของโค้ดเบสใหม่สำหรับแต่ละสถาปัตยกรรม

เมื่อพิจารณาถึงระดับ Wasm ในระดับต่ำแล้ว การลองใช้วิธีเดียวกันนี้ตามปกตินั้นเป็นเรื่องปกติ นั่นคือการคอมไพล์โค้ด VM หลักอีกครั้งด้วยโปรแกรมแยกวิเคราะห์ การสนับสนุนไลบรารี การรวบรวมกระบวนการจัดการ และเครื่องมือเพิ่มประสิทธิภาพให้กับ Wasm และใช้แบ็กเอนด์ JIT หรือ AOT สำหรับ Wasm หากจำเป็น ซึ่งเป็นไปได้นับตั้งแต่ที่ Wasm MVP และใช้งานได้ดีในหลายๆ กรณี ที่จริงแล้ว PHP คอมไพล์ไปยัง Wasm เป็นพลังขับเคลื่อน WordPress Playground ดูข้อมูลเพิ่มเติมเกี่ยวกับโครงการได้ในบทความสร้างประสบการณ์การใช้งาน WordPress ในเบราว์เซอร์ด้วย WordPress Playground และ WebAssembly

อย่างไรก็ตาม PHP Wasm จะทำงานในเบราว์เซอร์ในบริบทของ JavaScript ภาษาโฮสต์ ใน Chrome JavaScript และ Wasm ทำงานใน V8 ซึ่งเป็นเครื่องมือ JavaScript แบบโอเพนซอร์สของ Google ที่ใช้ ECMAScript ตามที่ระบุไว้ใน ECMA-262 และ V8 ก็มีพนักงานเก็บขยะอยู่แล้ว ซึ่งหมายความว่านักพัฒนาซอฟต์แวร์ที่ใช้ประโยชน์จากซอฟต์แวร์ เช่น PHP ซึ่งคอมไพล์ไปยัง Wasm จะจบลงด้วยการส่งเครื่องมือเก็บขยะสำหรับภาษาที่ย้ายมา (PHP) ไปยังเบราว์เซอร์ที่มีเครื่องมือเก็บขยะอยู่แล้ว ซึ่งก็จะไร้ประโยชน์มากพอๆ กัน WasmGC เข้ามาช่วยในเรื่องนี้

ปัญหาอีกอย่างหนึ่งของการให้โมดูล Wasm สร้าง GC ของตัวเองทับหน่วยความจำเชิงเส้นของ Wasm ก็คือจะไม่มีการโต้ตอบระหว่างเครื่องมือเก็บขยะของ Wasm เองและเครื่องมือเก็บขยะในตัวของภาษาที่คอมไพล์เป็น Wasm ซึ่งมักจะทำให้เกิดปัญหา เช่น หน่วยความจำรั่วไหลและการพยายามเก็บข้อมูลที่ไม่มีประสิทธิภาพ การปล่อยให้โมดูล Wasm นำ GC ที่มีอยู่ในตัวที่มีอยู่มาใช้ซ้ำเพื่อหลีกเลี่ยงปัญหาเหล่านี้

การพอร์ตภาษาโปรแกรมไปยังรันไทม์ใหม่ด้วย WasmGC

WasmGC เป็นข้อเสนอของกลุ่มชุมชน WebAssembly การใช้ MVP ของ Wasm ในปัจจุบันจัดการได้เฉพาะตัวเลขเท่านั้น ซึ่งก็คือจำนวนเต็มและเลขทศนิยมในหน่วยความจำเชิงเส้น และเมื่อมีการจัดส่งข้อเสนอประเภทการอ้างอิง แล้ว Wasm ก็สามารถเก็บข้อมูลอ้างอิงภายนอกเพิ่มเติมได้ ปัจจุบัน WasmGC ได้เพิ่มประเภทฮีปของโครงสร้างและอาร์เรย์ ซึ่งหมายความว่ามีการรองรับการจัดสรรหน่วยความจำแบบไม่เชิงเส้น ออบเจ็กต์ WasmGC แต่ละรายการมีประเภทและโครงสร้างที่คงที่ ซึ่งช่วยให้ VM สร้างโค้ดที่มีประสิทธิภาพเพื่อเข้าถึงช่องของตนได้ง่ายขึ้นโดยไม่ต้องเสี่ยงกับการลดประสิทธิภาพที่ภาษาแบบไดนามิกอย่าง JavaScript มี ด้วยเหตุนี้ ข้อเสนอนี้จึงเพิ่มการสนับสนุนที่มีประสิทธิภาพสำหรับภาษาที่มีการจัดการระดับสูงไปยัง WebAssembly ผ่านประเภทฮีปของโครงสร้างและอาร์เรย์ที่ทำให้คอมไพเลอร์ภาษาที่กำหนดเป้าหมาย Wasm ผสานรวมกับตัวเก็บขยะใน VM ของโฮสต์ได้ พูดง่ายๆ ก็คือเมื่อใช้ WasmGC การพอร์ตภาษาในการเขียนโปรแกรมเป็น Wasm จะหมายความว่าเครื่องมือรวบรวมขยะของภาษาโปรแกรมไม่จำเป็นต้องเป็นส่วนหนึ่งของพอร์ตอีกต่อไป แต่สามารถใช้เครื่องมือรวบรวมขยะที่มีอยู่แทนได้

เพื่อยืนยันผลกระทบจริงของการปรับปรุงนี้ ทีม Wasm ของ Chrome ได้รวบรวมการเปรียบเทียบของแฟนกูช (ซึ่งจัดสรรโครงสร้างข้อมูลตามความเหมาะสม) จาก C, Rust และ Java ไบนารี C และ Rust อาจอยู่ที่ใดก็ได้ตั้งแต่ 6.1 K ถึง 9.6 K โดยขึ้นอยู่กับแฟล็กของคอมไพเลอร์ที่หลากหลาย ในขณะที่เวอร์ชัน Java จะมีขนาดเล็กกว่ามากด้วยเพียง 2.3 K เท่านั้น C และ Rust ไม่รวมเครื่องมือเก็บข้อมูลขยะ แต่จะยังรวม malloc/free เพื่อจัดการหน่วยความจำอยู่ และเหตุผลที่ Java มีขนาดเล็กลงก็เนื่องจากไม่จำเป็นต้องรวมโค้ดการจัดการหน่วยความจำใดๆ เลย นี่เป็นเพียงตัวอย่างหนึ่งเท่านั้น แต่ก็แสดงให้เห็นว่าไบนารี WasmGC มีศักยภาพที่จะมีขนาดเล็กมาก และนี่ก็เกิดขึ้นก่อนที่จะมีการเพิ่มประสิทธิภาพขนาดอย่างมีนัยสำคัญเสียอีก

การดูการใช้ภาษาโปรแกรมที่รองรับ WasmGC

คอทลิน วาสเอ็ม

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 แบบหลายแพลตฟอร์มจะเริ่มมีความสมเหตุสมผลมากขึ้น ซึ่งช่วยให้นักพัฒนาแอปสร้างจาก UI ที่ตนอาจสร้างขึ้นสำหรับแอป Android Kotlin อยู่แล้ว ตรวจสอบการสำรวจเบื้องต้นด้วยโปรแกรมดูรูปภาพ Kotlin/Wasm และสำรวจซอร์สโค้ดที่ได้รับความเอื้อเฟื้อจากทีม Kotlin เช่นกัน

Dart และ Flutter

ทีม Dart และ Flutter ที่ Google กำลังเตรียมการสนับสนุนสำหรับ WasmGC ด้วย งานคอมไพล์ Dart-to-Wasm ใกล้เสร็จสมบูรณ์แล้ว และทีมงานกำลังดำเนินการสนับสนุนด้านเครื่องมือเพื่อให้บริการเว็บแอปพลิเคชัน Flutter ที่คอมไพล์ไปยัง WebAssembly อ่านสถานะปัจจุบันของงานได้ในเอกสารประกอบของ Flutter การสาธิตต่อไปนี้คือ Flutter WasmGC Preview

ดูข้อมูลเพิ่มเติมเกี่ยวกับ WasmGC

บล็อกโพสต์นี้แทบจะไม่มีรอยขีดข่วนและส่วนใหญ่ให้ภาพรวมระดับสูงของ WasmGC ด้วย ดูข้อมูลเพิ่มเติมเกี่ยวกับฟีเจอร์นี้ได้ที่ลิงก์ต่อไปนี้

กิตติกรรมประกาศ

รูปภาพหลักโดย Gary Chan ใน Unsplash บทความนี้ได้รับการตรวจสอบโดย Matthias Liedtke, Adam Klein, Joshua Bell, Alon Zakai, Jakob Kummerow, Clemens Backes, Emanuel Ziegler และ Rachel Andrew