ภาษาโปรแกรมมี 2 ประเภท ได้แก่ ภาษาโปรแกรมที่มีการรวบรวมขยะและภาษาโปรแกรมที่ต้องจัดการหน่วยความจำด้วยตนเอง ตัวอย่างภาษาแบบแรก ได้แก่ Kotlin, PHP หรือ Java ตัวอย่างกรณีหลังคือ C, C++ หรือ Rust โดยทั่วไปแล้ว ภาษาโปรแกรมระดับสูงมีแนวโน้มที่จะมีการรวบรวมขยะเป็นฟีเจอร์มาตรฐานมากกว่า บล็อกโพสต์นี้จะมุ่งเน้นไปที่ภาษาโปรแกรมที่มีการรวบรวมขยะดังกล่าวและวิธีคอมไพล์เป็น WebAssembly (Wasm) แต่การรวบรวมขยะ (มักเรียกว่า GC) คืออะไร
การรองรับเบราว์เซอร์
การเก็บขยะ
กล่าวอย่างง่ายคือ แนวคิดของการรวบรวมขยะคือการพยายามเรียกคืนหน่วยความจำที่โปรแกรมจัดสรรไว้ แต่ไม่มีการอ้างอิงอีกต่อไป หน่วยความจำดังกล่าวเรียกว่า "ขยะ" การใช้การเก็บขยะมีกลยุทธ์มากมาย หนึ่งในจำนวนดังกล่าวคือการนับการอ้างอิงซึ่งวัตถุประสงค์คือการนับจำนวนการอ้างอิงไปยังออบเจ็กต์ในหน่วยความจำ เมื่อไม่มีการอ้างอิงถึงออบเจ็กต์อีกต่อไป ระบบจะทำเครื่องหมายออบเจ็กต์ว่าไม่ได้ใช้งานแล้วและพร้อมสำหรับการเก็บขยะ เครื่องมือเก็บขยะของ 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 การใช้งาน Wasm MVP ปัจจุบันสามารถจัดการกับตัวเลขเท่านั้น ซึ่งก็คือจำนวนเต็มและทศนิยมในหน่วยความจําแบบเส้นตรง และเมื่อมีการส่งข้อเสนอประเภทข้อมูลอ้างอิงแล้ว Wasm จะเก็บข้อมูลอ้างอิงภายนอกได้อีกด้วย ตอนนี้ WasmGC ได้เพิ่มประเภทกอง STRUCT และ Array ซึ่งหมายความว่ารองรับการจัดสรรหน่วยความจำแบบไม่เป็นเส้นตรง ออบเจ็กต์ WasmGC แต่ละรายการมีประเภทและโครงสร้างแบบคงที่ ซึ่งทำให้ VM สร้างโค้ดที่มีประสิทธิภาพเพื่อเข้าถึงฟิลด์ต่างๆ ได้ง่ายโดยไม่ต้องเสี่ยงกับการปรับประสิทธิภาพลงที่ภาษาแบบไดนามิกอย่าง JavaScript มี ด้วยเหตุนี้ ข้อเสนอนี้จึงเพิ่มการสนับสนุนที่มีประสิทธิภาพสำหรับภาษาที่มีการจัดการระดับสูงไปยัง WebAssembly ผ่านประเภทฮีปของโครงสร้างและอาร์เรย์ที่ทำให้คอมไพเลอร์ภาษาที่กำหนดเป้าหมาย Wasm ผสานรวมกับตัวเก็บขยะใน VM ของโฮสต์ได้ กล่าวอย่างง่ายคือ เมื่อใช้ WasmGC การพอร์ตภาษาโปรแกรมไปยัง Wasm จะทำให้ไม่จำเป็นต้องรวมโปรแกรมเก็บขยะของภาษาโปรแกรมนั้นไว้ในพอร์ตอีกต่อไป แต่จะใช้โปรแกรมเก็บขยะที่มีอยู่ได้
ทีม Wasm ของ Chrome ได้รวบรวมการทดสอบประสิทธิภาพ Fannkuch (ซึ่งจัดสรรโครงสร้างข้อมูลขณะทำงาน) เวอร์ชันต่างๆ จาก C, Rust และ Java เพื่อยืนยันผลกระทบในชีวิตจริงของการปรับปรุงนี้ ไฟล์ไบนารี C และ Rust มีขนาดตั้งแต่ 6.1 K ถึง 9.6 K โดยขึ้นอยู่กับ Flag ต่างๆ ของคอมไพเลอร์ ส่วนเวอร์ชัน Java จะมีขนาดเล็กกว่ามากเพียง 2.3 K C และ Rust ไม่มีเครื่องมือเก็บขยะ แต่ยังคงรวม malloc/free
เพื่อจัดการหน่วยความจำอยู่ และสาเหตุที่ Java มีขนาดเล็กกว่าก็เพราะว่าไม่ต้องรวมโค้ดการจัดการหน่วยความจำใดๆ เลย นี่เป็นเพียงตัวอย่างที่เฉพาะเจาะจงเพียงตัวอย่างเดียว แต่แสดงให้เห็นว่าไบนารีของ WasmGC มีแนวโน้มที่จะมีขนาดไฟล์เล็กมาก และนี่ยังอยู่ในช่วงก่อนการทํางานที่สำคัญใดๆ ในการเพิ่มประสิทธิภาพขนาด
การดูการใช้ภาษาโปรแกรมที่รองรับ WasmGC
คอทลิน วาสเอ็ม
หนึ่งในภาษาโปรแกรมแรกๆ ที่พอร์ตไปยัง Wasm ด้วย WasmGC คือ 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 ซึ่งช่วยให้นักพัฒนาแอปสร้าง UI ต่อจาก UI ที่อาจสร้างไว้แล้วสําหรับแอป Android Kotlin โปรดดูการสำรวจเบื้องต้นเกี่ยวกับเรื่องนี้ด้วยตัวอย่างโปรแกรมดูรูปภาพ Kotlin/Wasm และสำรวจซอร์สโค้ดของโปรแกรมดูรูปภาพดังกล่าว ซึ่งทีม Kotlin เป็นผู้จัดทำขึ้น
Dart และ Flutter
ทีม Dart และ Flutter ของ Google กำลังเตรียมการสนับสนุน WasmGC ด้วย การคอมไพล์ Dart เป็น WASM เกือบเสร็จสมบูรณ์แล้ว และทีมกำลังดำเนินการเกี่ยวกับการสนับสนุนเครื่องมือสำหรับนำส่งเว็บแอปพลิเคชัน Flutter ที่คอมไพล์เป็น WebAssembly อ่านสถานะปัจจุบันของงานได้ในเอกสารประกอบของ Flutter การสาธิตต่อไปนี้คือ Flutter WasmGC Preview
ดูข้อมูลเพิ่มเติมเกี่ยวกับ WasmGC
บล็อกโพสต์นี้เป็นเพียงข้อมูลเบื้องต้นและส่วนใหญ่ให้ภาพรวมระดับสูงของ WasmGC ดูข้อมูลเพิ่มเติมเกี่ยวกับฟีเจอร์นี้ได้ที่ลิงก์ต่อไปนี้
- วิธีใหม่ในการนําภาษาโปรแกรมที่มีการรวบรวมขยะมาใช้กับ WebAssembly อย่างมีประสิทธิภาพ
- ภาพรวมของ WaasmGC
- WasmGC MVP
- WasmGC หลัง MVP
ขอขอบคุณ
บทความนี้ได้รับการตรวจสอบโดย Matthias Liedtke, Adam Klein, Joshua Bell, Alon Zakai, Jakob Kummerow, Clemens Backes, Emanuel Ziegler และ Rachel Andrew