ข้อจำกัดของ WebAssembly และความสำคัญของ Tree-shaking
-
แม้ WebAssembly จะได้รับความสนใจและความคาดหวังอย่างมาก แต่ก็ประสบความสำเร็จบนเว็บได้อย่างจำกัด
- มีกรณีที่ประสบความสำเร็จอย่าง Photoshop แต่โดยรวมแล้วโปรเจ็กต์ที่ใช้ WebAssembly ยังมีไม่มาก
- โดยเฉพาะแอปที่ใช้ DOM อย่างหนักนั้น WebAssembly ไม่ค่อยเหมาะนัก
- ความแตกต่างของโมเดลการเขียนโปรแกรมระหว่าง JavaScript และ WebAssembly เป็นหนึ่งในสาเหตุหลัก
-
นอกจากภาษาอย่าง C หรือ Rust แล้ว WebAssembly ยังไม่ค่อยประสบความสำเร็จมากนัก
- ภาษาประเภท C# มีความไม่สะดวกตรงที่ต้องพ่วงรันไทม์อย่าง garbage collector มาด้วย
- แต่คาดว่าสถานการณ์จะดีขึ้น เพราะฟีเจอร์ใหม่ของ WebAssembly ที่รองรับ reference type และ garbage collection กำลังจะถูกนำมาใช้ในเร็ว ๆ นี้
ความสามารถในการปรับโค้ดให้เหมาะสมของคอมไพเลอร์คือกุญแจสู่ความสำเร็จของ WebAssembly
-
หาก WebAssembly จะประสบความสำเร็จบนเว็บ คอมไพเลอร์ต้องสร้างโค้ดที่เล็กและมีประสิทธิภาพได้
- การรักษาขนาดไฟล์ให้เล็กเพียงไม่กี่กิโลไบต์เป็นเรื่องสำคัญ
- ไม่เช่นนั้นก็อาจต้องพึ่งพาเพียงกระแสเกินจริงหรือฐานผู้ใช้เฉพาะกลุ่ม
-
ในโลกของ JavaScript มีการทำ optimization ด้านขนาดโค้ดผ่าน bundler และเครื่องมืออื่น ๆ อยู่แล้ว
- Tree-shaking คือเทคนิคที่รวมเฉพาะฟังก์ชันและชนิดข้อมูลที่ถูกใช้งานจริงในโปรแกรมเท่านั้น
-
แม้ Tree-shaking จะเป็นอุปมาอุปไมยที่ไม่ค่อยเหมาะทั้งในเชิงพืชสวนและเชิงอัลกอริทึม แต่ก็เป็นคำที่ใช้กันอย่างแพร่หลาย
สถานะของ Tree-shaking ในภาษาอื่น ๆ
-
ในภาษาที่มีรันไทม์หนักอย่าง Go หรือ Python นั้น Tree-shaking ยังไม่ได้รับการปรับให้เหมาะสมมากนัก
- แม้แต่โปรแกรม Go ที่ง่ายที่สุด เมื่อนำไปคอมไพล์เป็น WebAssembly ก็ยังมีขนาดเกิน 2MB
- ส่วน Pyodide ของ Python ก็ต้องดาวน์โหลดไฟล์ราว 20MB
-
เพราะในสภาพแวดล้อมแบบเซิร์ฟเวอร์ ขนาดไบนารีมักไม่ใช่ปัญหาใหญ่
- สำหรับสภาพแวดล้อมที่มีข้อจำกัดอย่างมือถือ จึงมีการพัฒนา toolchain แบบเบาอย่าง MicroPython และ TinyGo แยกต่างหาก
-
ตัวภาษาในเวอร์ชันสำหรับเว็บย่อมหลีกเลี่ยงไม่ได้ที่จะต่างจากของเดิม
- เพราะการโต้ตอบกับ DOM เองก็เป็นสภาพแวดล้อมที่ค่อนข้างพิเศษ
- ในกรณีของ ClojureScript ก็มีการจัดทำเอกสารแยกต่างหากเกี่ยวกับความแตกต่างจาก Clojure
ประเด็นถกเถียงเกี่ยวกับอัลกอริทึม Tree-shaking
-
คอมไพเลอร์ Hoot Scheme ที่ผู้เขียนกำลังพัฒนาอยู่ในตอนนี้ สร้างโค้ด Wasm ได้ราว 70KB
- การรวมเฉพาะนิยามของฟังก์ชัน (procedure) นั้นค่อนข้างทำได้ไม่ยาก
- แต่ยังมีจุดยากอยู่หลายประการดังนี้
-
ในโมเดลการประเมินผล
letrec*นั้น binding มีทั้งความเป็น recursive และมีลำดับ จึงทำให้คอมไพเลอร์วิเคราะห์ได้ยาก- ในกรณีของ record type นั้น vtable callback ทำให้ต้องเก็บโค้ดจำนวนมากเอาไว้
-
หากใช้ฟังก์ชันที่มีความเป็น polymorphic สูงอย่าง
displayก็จะดึงโค้ดที่เกี่ยวข้องจำนวนมากเข้ามาด้วย- การใช้ฟังก์ชันที่เฉพาะเจาะจงอย่าง
write-stringจะดีกว่า
- การใช้ฟังก์ชันที่เฉพาะเจาะจงอย่าง
-
หากต้องการ Tree-shaking ที่เหมาะสมที่สุด จำเป็นต้องมี flow analysis
- ถ้ารู้ได้ว่า
displayจะไม่ถูกส่งอาร์กิวเมนต์เป็น bitvector ก็สามารถลบโค้ดที่เกี่ยวข้องออกได้
- ถ้ารู้ได้ว่า
-
ใน Python เรื่องนี้ยิ่งยากกว่า เพราะมีฟีเจอร์แบบ dynamic อย่าง dynamic dispatch,
__getattr__เป็นต้น- โครงสร้างโมดูลของ Python เองก็เป็นอีกปัจจัยที่ทำให้ Tree-shaking ซับซ้อนขึ้น
สรุป
- การรองรับ GC ทำให้สามารถเขียนโปรแกรม DOM บน WebAssembly ด้วยภาษาอื่นนอกเหนือจาก JavaScript ได้
- แต่หากต้องการทำให้ผลลัพธ์มีขนาดเล็กพอ ก็ยังต้องลงทุนอย่างมากกับ toolchain ของแต่ละภาษา
- จำเป็นต้องมีทั้งการพัฒนา toolchain แยกต่างหากที่ใช้อัลกอริทึม Tree-shaking และการปรับแต่ง standard library ให้เหมาะสม
ความเห็นของ GN⁺
-
เมื่อ WebAssembly รองรับ GC ก็ทำให้สามารถใช้ภาษาที่หลากหลายในการพัฒนาเว็บได้มากขึ้น แต่ดูเหมือนว่ายังมีอุปสรรคมากมายหากจะยก toolchain ของภาษาเดิมมาใช้ตรง ๆ คงต้องมีทั้ง implementation ของภาษาและเทคนิค optimization ที่ออกแบบมาเฉพาะสำหรับสภาพแวดล้อมเว็บ
-
หากต้องการให้ Tree-shaking ทำงานได้ดีในภาษาที่เป็น dynamic typing ก็ดูเหมือนว่า static analysis จะเป็นสิ่งจำเป็น แต่สำหรับภาษาอย่าง Python ที่มีฟีเจอร์แบบ dynamic มาก ก็อาจไม่ใช่เรื่องง่าย บางทีการออกแบบภาษาใหม่ที่เอื้อต่อ static analysis ตั้งแต่ต้นก็อาจเป็นอีกแนวทางหนึ่ง
-
โปรเจ็กต์เชิงทดลองอย่าง Hoot หรือ TinyGo น่าจะเป็นตัวอย่างอ้างอิงที่ดี แต่การนำโปรเจ็กต์เหล่านี้ไปใช้กับผลิตภัณฑ์จริงอาจยังเร็วเกินไป คงต้องค่อย ๆ ปรับปรุงกันไปทีละขั้น
-
ถ้าเป็นโปรเจ็กต์ที่ไม่ได้ไวต่อประสิทธิภาพมากนักและให้ความสำคัญกับการพัฒนาอย่างรวดเร็ว การใช้ Pyodide ก็น่าลองอยู่ แต่ถ้าเป็นผลิตภัณฑ์ที่ให้ความสำคัญกับประสบการณ์ผู้ใช้ ตอนนี้ JavaScript ก็ดูจะยังเป็นตัวเลือกที่ดีที่สุด
-
นอกจากนี้ยังอาจลองคิดถึงการใส่ความสามารถแบบ Tree-shaking เข้าไปในตัว WebAssembly เองได้เหมือนกัน แต่เพราะแต่ละภาษามีความต้องการต่างกัน เรื่องนี้จึงไม่น่าจะง่ายนัก และถ้าในอนาคตมีภาษาที่รองรับ Tree-shaking ได้ดีมาก ๆ การเขียนโค้ดด้วยภาษานั้นโดยตรงอาจจะคุ้มกว่า ก็ยังน่าสนใจว่าบทบาทระหว่าง WebAssembly กับภาษาโปรแกรมจะถูกแบ่งกันอย่างไรในท้ายที่สุด
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
สรุปได้ดังนี้:
floatVecแทนHashMaptalc)rand,fxhash)mainและรวมเฉพาะโค้ดที่เข้าถึงได้ลงในไบนารีสุดท้าย