- ใน Go 1.24 มีการขยายความสามารถที่เกี่ยวข้องกับ WebAssembly (Wasm)
- มีการเพิ่ม directive
go:wasmexportทำให้สามารถเรียกฟังก์ชัน Go จากภายนอกโมดูล Wasm ได้ - ยังรองรับโหมด build แบบ “reactor” สำหรับ WASI ทำให้สามารถรันโค้ดในสถานะที่ทำงานต่อเนื่องได้เป็นเวลานาน
- สิ่งนี้เปิดทางให้ขยายแอปพลิเคชัน Go ในสภาพแวดล้อม Wasm ได้อย่างยืดหยุ่นมากขึ้น
WebAssembly and the WebAssembly System Interface
- WebAssembly เป็นฟอร์แมตไบนารีที่สร้างขึ้นเพื่อรันโค้ดระดับล่างประสิทธิภาพสูงในเว็บเบราว์เซอร์
- ปัจจุบันถูกนำไปใช้อย่างกว้างขวางนอกเบราว์เซอร์ด้วยเช่นกัน และสามารถโต้ตอบกับทรัพยากรของระบบผ่าน WebAssembly System Interface (WASI) ได้
- Go เริ่มรองรับการคอมไพล์ไปยัง Wasm ผ่านพอร์ต js/wasm ตั้งแต่เวอร์ชัน 1.11 และในเวอร์ชัน 1.21 ได้เพิ่มพอร์ตใหม่ผ่าน GOOS=wasip1 เพื่อกำหนดเป้าหมาย API system call ของ WASI preview 1
Export ฟังก์ชัน Go เป็น Wasm ด้วย go:wasmexport
- ใน Go 1.24 มีการเพิ่ม directive
go:wasmexportใหม่ ทำให้สามารถเปิดเผยฟังก์ชัน Go เป็น export เพื่อให้เรียกจากภายนอกโมดูล Wasm ได้ - ตัวอย่างเช่น หากประกาศ
//go:wasmexport addแล้วเขียนฟังก์ชันตามนั้น Wasm host ก็จะสามารถเรียกฟังก์ชันดังกล่าวได้ - แนวทางนี้คล้ายกับ directive
exportของ cgo แต่ถูกทำให้เรียบง่ายกว่า
Building a WASI Reactor
- WASI “reactor” หมายถึงโมดูล WebAssembly ที่ทำงานต่อเนื่องและตอบสนองต่อ event หรือ request ได้
- ใน Go 1.24 รองรับการ build แบบ WASI reactor ด้วยออปชัน
-buildmode=c-shared - build flag นี้จะสั่ง linker ไม่ให้สร้างฟังก์ชัน _start (entry point ของ command module) แต่ให้สร้างฟังก์ชัน _initialize แทน
- reactor จะถูกเริ่มต้นผ่านฟังก์ชัน
_initializeและต้องเรียกฟังก์ชันนี้ก่อนแทนที่จะเริ่มที่ฟังก์ชัน main
- reactor จะถูกเริ่มต้นผ่านฟังก์ชัน
- เมื่อนำไปใช้ร่วมกับ runtime อย่าง Wazero หลังจากเรียก
_initializeแล้ว ก็สามารถเรียกฟังก์ชันที่ export ไว้ซ้ำได้ตามต้องการ - วิธีนี้มีประโยชน์ในสภาพแวดล้อมที่ใช้ Wasm เป็นกลไกปลั๊กอินหรือส่วนขยายของแอปพลิเคชัน
รองรับชนิดข้อมูลระหว่างโฮสต์และไคลเอนต์ได้หลากหลายขึ้น
- ใน Go 1.24 ข้อจำกัดของชนิดข้อมูลสำหรับพารามิเตอร์/ค่าที่ส่งกลับของฟังก์ชันที่เรียกผ่าน
go:wasmimportถูกผ่อนคลายลง - ตัวอย่างเช่น สามารถส่งค่า bool, string, พอยน์เตอร์ int32, พอยน์เตอร์โครงสร้างข้อมูล เป็นต้น ได้
- อย่างไรก็ตาม ยังมีข้อจำกัดอยู่จากความแตกต่างระหว่างสภาพแวดล้อม 64 บิตและ 32 บิต เป็นต้น
- สิ่งนี้ช่วยให้เขียนแอปพลิเคชัน Go Wasm ได้เป็นธรรมชาติและสะดวกยิ่งขึ้น พร้อมลดการแปลงชนิดข้อมูลที่ไม่จำเป็น
ข้อจำกัด
- Wasm เป็นสถาปัตยกรรมแบบเธรดเดียวที่ไม่มีการประมวลผลแบบขนาน
- ฟังก์ชัน
go:wasmexportสามารถสร้าง goroutine ใหม่ได้ แต่ถ้าฟังก์ชันนั้นสร้าง goroutine ที่ทำงานเบื้องหลัง มันจะไม่ทำงานต่อหลังจากgo:wasmexportคืนค่าแล้ว จนกว่าจะมีการเรียกกลับเข้าสู่โมดูล Wasm ที่สร้างด้วย Go อีกครั้ง - แม้ว่าข้อจำกัดบางส่วนของชนิดข้อมูลจะถูกผ่อนคลายแล้ว แต่ชนิดข้อมูลที่ใช้ร่วมกับฟังก์ชัน
go:wasmimportและgo:wasmexportได้ก็ยังคงมีข้อจำกัดอยู่- การส่งชนิดข้อมูลเชิงประกอบที่มีพอยน์เตอร์ยังคงมีข้อจำกัด
บทสรุป
- การเพิ่มความสามารถในการ build แบบ WASI reactor และฟีเจอร์
go:wasmexportใน Go 1.24 เป็นการปรับปรุงที่ช่วยขยาย ecosystem ของ Go บน Wasm อย่างมาก - สิ่งนี้ช่วยให้นักพัฒนาสามารถสร้างแอปพลิเคชัน Wasm ที่อิงกับ Go ได้หลากหลายยิ่งขึ้น และเปิดความเป็นไปได้ใหม่ให้ Go ใน ecosystem ของ Wasm
3 ความคิดเห็น
ก่อนที่ Wasm/gc จะถูกนำมาใช้อย่างแพร่หลาย ผมคิดว่าน่าจะดีกว่าถ้าพัฒนาเป้าหมาย wasm ด้วยภาษาที่ไม่มี gc
ในโน้ตการออก Go 1.24 อธิบายไว้สั้น ๆ แต่เป็นอัปเดตที่สำคัญกว่านั้นมากครับ
ความเห็นจาก Hacker News
มีปัญหาใหญ่ตรงที่ไบนารี WASM ที่สร้างด้วย Go มีขนาดใหญ่มาก TinyGo ช่วยแก้ปัญหานี้ได้ แต่คอมไพล์ช้าและต้องระวังเรื่องการเลือกไลบรารี ถ้าจะเอาชนะข้อจำกัดของทั้งสองฝั่งได้ก็ต้องใช้ความอดทนมาก
hello-worldยังรันได้ แต่พอซับซ้อนกว่านั้นก็เกินข้อจำกัดด้านขนาดน่าทึ่งมาก เรื่องที่ควรจำไว้คือ:
จำไม่ค่อยได้ว่าก่อน Go 1.24 จะยังไม่สามารถ export ฟังก์ชัน Go ไปยัง JS ได้หรือไม่ เหมือนจะจำได้ว่าเมื่อก่อนก็เรียกใช้ฟังก์ชัน Go ที่ export ไว้จาก JS ได้ไม่มีปัญหา
goos=wasip1นี้ยังใช้ได้อยู่หรือไม่ดูเหมือนว่าจะ "เป็น Go" มากกว่าถ้า export ฟังก์ชันทั้งหมดที่ขึ้นต้นด้วยตัวพิมพ์ใหญ่ในแพ็กเกจ main เพราะการ export ทำงานแบบนี้ในภาษาอยู่แล้ว จึงควรใช้ compiler directive เฉพาะตอนที่ต้องการตั้งชื่ออย่างชัดเจนให้กับสิ่งที่ขึ้นต้นด้วยตัวพิมพ์เล็ก
ไม่มีการพูดถึงการทำงานร่วมกับ WASM Component Model
สงสัยว่าการทำ garbage collection ของ Go และ WASM ทำงานอย่างไร
อยากให้มีภาษาระดับล่างที่มี strong type และรองรับ WASM ได้ยอดเยี่ยม
สงสัยว่าจะ debug โมดูล WASM ที่กำลังรันอยู่ในโปรแกรมโฮสต์อย่างไร
กังวลว่าความต้องการฟีเจอร์ WASM เพิ่มเติมอาจทำลาย ecosystem ที่ยังอายุน้อยอย่างถาวรได้ ฟีเจอร์ส่วนใหญ่ที่ Go เพิ่มเข้าไปใน WASM น่าจะทำได้แบบ native อยู่แล้วถ้าข้อเสนอ Component Model ถูกรวมเข้ามาก่อนหน้านี้