Binaryen และ wasm-opt
เมื่อ binary .wasm ที่ compile ออกมาจาก Rust, C หรือ AssemblyScript มีขนาดใหญ่เกินไปหรือทำงานช้ากว่าที่ควร นี่คือจุดที่ทีมส่วนใหญ่หันมาใช้ Binaryen เพื่อบีบขนาดและเพิ่ม performance ให้ binary ที่ compile เสร็จแล้ว โดยไม่ต้องแตะ source code เลย
Binaryen คือโครงสร้างพื้นฐาน optimising compiler สำหรับ WebAssembly ของ Google เครื่องมือที่ใช้กันมากที่สุดคือ wasm-opt ซึ่งรับ binary .wasm ที่มีอยู่และเขียนใหม่เพื่อผลิต output ที่เล็กลงหรือเร็วขึ้น คุณไม่จำเป็นต้องเข้าใจ IR ภายในของ Binaryen เพื่อได้รับประโยชน์จากเครื่องมือนี้ — คำสั่งเดียวสามารถลด release binary ได้ 20–40%
สิ่งที่ wasm-opt ทำ
หัวข้อที่มีชื่อว่า “สิ่งที่ wasm-opt ทำ”wasm-opt ใช้ลำดับของ compiler passes: dead-code elimination, constant folding, function inlining, local CSE และอื่นๆ อีกมาก ทำงานในระดับ binary ทั้งหมด — ไม่จำเป็นต้องมีต้นฉบับหรือ metadata ใดๆ
# ติดตั้งทั่วโลกผ่าน npmnpm install -g binaryen
# หรือติดตั้งผ่าน Homebrew บน macOSbrew install binaryenระดับการ optimise
หัวข้อที่มีชื่อว่า “ระดับการ optimise”wasm-opt ใช้ flags ระดับเดียวกับ Clang/GCC:
# -O1 — การ optimise เบา (build รวดเร็ว)wasm-opt -O1 input.wasm -o output.wasm
# -O2 — สมดุล (เหมือนกับ release builds ในส่วนใหญ่ของ toolchains)wasm-opt -O2 input.wasm -o output.wasm
# -O3 — เชิงรุก (เพิ่มความเร็วสูงสุด, code อาจใหญ่ขึ้น)wasm-opt -O3 input.wasm -o output.wasm
# -Os — optimise เพื่อขนาด (ความเร็วรอง)wasm-opt -Os input.wasm -o output.wasm
# -Oz — ลดขนาดสูงสุด (การ optimise ช้าที่สุด)wasm-opt -Oz input.wasm -o output.wasmสำหรับการ deploy production ส่วนใหญ่ -Oz ให้การแลกเปลี่ยนที่ดีที่สุด: download ที่เล็กลง parse ที่เร็วขึ้น cache ที่อุ่นขึ้น
Toolchains เรียกใช้ wasm-opt ให้คุณ
หัวข้อที่มีชื่อว่า “Toolchains เรียกใช้ wasm-opt ให้คุณ”ในทางปฏิบัติคุณแทบไม่เคยเรียก wasm-opt โดยตรง toolchains ยอดนิยมเหล่านี้เรียกใช้ให้อัตโนมัติในโหมด release:
| Toolchain | เมื่อ wasm-opt ทำงาน |
|---|---|
| Emscripten | flags release -O2 / -O3 |
| wasm-pack (Rust) | wasm-pack build --release |
| AssemblyScript | asc --optimize |
| Go (TinyGo) | flag -opt=z |
หากคุณใช้ .wasm จากสิ่งเหล่านี้ binary น่าจะถูก optimise แล้ว การเรียก wasm-opt ครั้งที่สองนั้นปลอดภัยแต่ได้ผลลัพธ์ที่ลดน้อยลง
Flags เพิ่มเติมที่มีประโยชน์
หัวข้อที่มีชื่อว่า “Flags เพิ่มเติมที่มีประโยชน์”# ลบ DWARF debug section (ลดขนาด สูญเสีย stack traces)wasm-opt -Oz --strip-dwarf input.wasm -o output.wasm
# ลบ custom sections ทั้งหมดwasm-opt -Oz --strip-producers input.wasm -o output.wasm
# แสดงสถิติโมดูลก่อนและหลังwasm-opt -Oz --print-stats input.wasm -o output.wasm
# เก็บ name section สำหรับ stack traces ที่อ่านได้wasm-opt -Oz --debuginfo input.wasm -o output.wasmการวัดผล
หัวข้อที่มีชื่อว่า “การวัดผล”วัดผลก่อนและหลังเสมอ one-liner bash ง่ายๆ แสดงความแตกต่างของขนาด:
wasm-opt -Oz input.wasm -o output.wasmecho "Before: $(wc -c < input.wasm) bytes"echo "After: $(wc -c < output.wasm) bytes"ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
wasm-opt -Oz (การ optimise เชิงรุกสุด) | ได้ทั้ง size และ speed ที่ดีที่สุด | build time นานขึ้น และ mapping ระหว่าง source กับ output เพี้ยนจน debug ยากขึ้น |
เก็บ --debug-names ไว้ | อ่าน stack trace ได้ ทำให้ debug ง่ายขึ้นมาก | เพิ่มขนาด binary ที่ต้อง ship ให้ผู้ใช้จริง |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- ข้าม
wasm-optไปเลยแล้ว ship.wasmที่ compiler สร้างออกมาตรงๆ พลาดโอกาสลด size/speed ที่ได้มาแทบฟรีๆ - เรียก
-O3หรือ-Ozแบบสุ่มสี่สุ่มห้าโดยไม่วัดผลก่อน-หลัง ทำให้ไม่รู้เลยว่า flag ไหนช่วยจริงและช่วยแค่ไหน - Strip
--debug-namesออกตั้งแต่ dev build ทำให้เวลาต้อง debug crash จริงๆ กลับไม่มี stack trace ที่อ่านออก
💡 ตัวอย่างจากของจริง
Emscripten เรียก
wasm-optของ Binaryen ให้อัตโนมัติเมื่อ build ด้วย-O2/-O3ส่วนทีมที่ ship ไปที่ Cloudflare Workers/CDN มักให้ความสำคัญกับ budget ของขนาด binary เป็นพิเศษ จึงนิยมใช้wasm-opt -Ozกันเป็นมาตรฐาน