2 คะแนน โดย GN⁺ 2025-10-05 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • การเปิดตัว Zig 0.15.1 ทำให้ ความเร็วในการคอมไพล์ ดีขึ้นอย่างมากเมื่อเทียบกับเวอร์ชันก่อนหน้า
  • จากการวัดเวลาบิลด์จริงใน โปรเจ็กต์ Ghostty พบว่า เวลาในการทำงานโดยรวมสั้นลง
  • แม้ยังคง ใช้ LLVM บางส่วน อยู่ แต่ก็มีความคาดหวังสูงว่าจะเร็วขึ้นได้อีกเมื่อใช้แบ็กเอนด์ของตัวเอง
  • การคอมไพล์แบบ incremental ยังไม่ถูกทำให้สมบูรณ์ทั้งหมด แต่ก็เริ่มเห็นข้อได้เปรียบด้านประสิทธิภาพแม้ในงานบิลด์บางส่วน
  • ในอนาคตมีความเป็นไปได้สูงที่จะได้สภาพแวดล้อมการบิลด์ที่เร็วขึ้น และ ประสบการณ์การพัฒนาที่ดีขึ้น

ภาพรวม

จากคำพูดของ Andrew Kelley ที่ว่า "คอมไพเลอร์ช้าเกินไปจนทำให้เกิดบั๊ก" Zig จึงเดินหน้าปรับโครงสร้างหลายด้านตลอดหลายปีที่ผ่านมาโดยมีเป้าหมายคือ เวลาในการคอมไพล์ที่เร็วขึ้น

  • ทีม Zig พยายาม ถอด LLVM ออก, พัฒนา แบ็กเอนด์สร้างโค้ด ของตัวเอง, สร้าง ลิงเกอร์ ของตัวเอง และท้ายที่สุดคือทำให้ การคอมไพล์แบบ incremental เกิดขึ้นจริง
  • ผลลัพธ์ของการพัฒนาระยะยาวนี้เริ่มปรากฏอย่างชัดเจนใน Zig 0.15.1 และมีการวัดพร้อมแชร์การเปลี่ยนแปลงของเวลาบิลด์ในโปรเจ็กต์จริงอย่าง Ghostty

ความเร็วในการคอมไพล์ Build Script

  • Zig 0.14: 7 วินาที 167 มิลลิวินาที
  • Zig 0.15: 1 วินาที 702 มิลลิวินาที

นี่คือเวลาบิลด์ของสคริปต์ build.zig เอง ซึ่งเป็นต้นทุนเริ่มต้นที่ต้องจ่ายทุกครั้งในสภาพแวดล้อมบิลด์ซอร์สใหม่

  • ความถี่ในการคอมไพล์สคริปต์บิลด์ซ้ำยังคงอยู่ในระดับต่ำ แต่ส่งผลโดยตรงต่อประสบการณ์ของผู้ใช้ที่บิลด์โปรเจ็กต์ด้วยตัวเองเป็นครั้งแรก

การบิลด์ไบนารีเต็มแบบไม่ใช้แคช (Ghostty)

  • Zig 0.14: 41 วินาที
  • Zig 0.15: 32 วินาที

นี่คือเวลาบิลด์ไบนารีทั้งหมดรวมเวลาในการบิลด์สคริปต์บิลด์ด้วย

  • Zig 0.15 ให้ผลด้านความเร็วเพิ่มขึ้นอีกราว 2 วินาที และยังเห็นความแตกต่างเริ่มต้นได้ชัดเจนแม้ดูจากเวลาแบบ wall-clock จริง
  • ขณะนี้ แบ็กเอนด์ x86_64 ของตัวเอง ยังไม่ถูกใช้งานได้อย่างเต็มที่ และส่วนใหญ่ยังคงใช้ LLVM ต่อไป
  • คาดว่าเมื่อ Ghostty บิลด์ด้วย แบ็กเอนด์ของตัวเอง ได้อย่างสมบูรณ์ในอนาคต เวลาจะลดลงต่ำกว่า 25 วินาทีได้ (ประมาณครึ่งหนึ่งของเดิม)

การบิลด์แบบ incremental (ไฟล์รันของ Ghostty)

  • Zig 0.14: 19 วินาที
  • Zig 0.15: 16 วินาที

นี่คือเวลาที่ใช้ในการบิลด์ใหม่หลังจากแก้ไขหนึ่งบรรทัดที่มีความหมาย (เพิ่ม log call ในโค้ด terminal emulation)

  • เป็นการบิลด์บางส่วนในสถานะที่สคริปต์บิลด์และกราฟ dependency ถูกแคชไว้แล้ว
  • แม้ ฟีเจอร์การคอมไพล์แบบ incremental จะยังไม่ถูกทำให้สมบูรณ์ แต่ก็เห็นการปรับปรุงด้านประสิทธิภาพได้ชัดเจนแล้ว
  • หากตัดส่วนที่ยังใช้ LLVM ออก ก็มีโอกาสลดลงได้ถึงราว 12 วินาที
  • หากมีการทำ incremental build ที่แท้จริงในอนาคต ก็อาจคาดหวังการบิลด์ระดับมิลลิวินาทีได้

การบิลด์แบบ incremental (libghostty-vt)

  • Zig 0.14: 2 วินาที 884 มิลลิวินาที
  • Zig 0.15: 975 มิลลิวินาที

เป็นการวัดเวลาที่ใช้บิลด์ใหม่บางส่วนเฉพาะ libghostty-vt หลังจากแก้ไขหนึ่งบรรทัด

  • libghostty-vt สามารถบิลด์ได้ทั้งหมดด้วย แบ็กเอนด์ x86_64 ของตัวเอง จึงสะท้อนการปรับปรุงของ Zig ได้โดยตรงโดยไม่ถูกรบกวนจาก LLVM
  • แม้ยังไม่ใช่ incremental compilation ก็ถือว่าการทำเวลาบิลด์ต่ำกว่า 1 วินาทีเป็นความก้าวหน้าที่สำคัญ
  • ช่วยเพิ่มประสิทธิภาพในเวิร์กโฟลว์ของนักพัฒนาด้วย ประสบการณ์ฟีดแบ็กที่แทบจะทันที
  • แบ็กเอนด์ x86_64 และ aarch64 กำลังเสถียรมากขึ้นเรื่อย ๆ และมีโอกาสถูกนำไปใช้กับ Ghostty ทั้งหมดภายในไม่กี่เดือน

สถานะปัจจุบันของการปรับปรุงความเร็วการบิลด์

  • การบิลด์ Ghostty ด้วย Zig 0.15.1 เร็วขึ้นอย่างชัดเจนในทุกช่วงการวัด
  • แม้แบ็กเอนด์ของตัวเองและการคอมไพล์แบบ incremental จะยังไม่เสร็จสมบูรณ์ แต่ผลงานในตอนนี้ก็น่าประทับใจเพียงพอแล้ว
  • ในอีก 1-2 ปีข้างหน้า มีแนวโน้มว่าจะได้เห็นผลลัพธ์ด้านความเร็วที่ก้าวกระโดดยิ่งกว่านี้
  • ทำให้รู้สึกได้ว่าการเลือก Zig เป็นทางเลือกที่สมเหตุสมผลในมุมมองของความเร็วในการบิลด์

1 ความคิดเห็น

 
GN⁺ 2025-10-05
ความคิดเห็นบน Hacker News
  • ตอนจบมัธยมปลายในปี 1995 เคยได้สัมผัสความเร็วคอมไพล์ที่เร็วมากพอๆ กับตอนรีคอมไพล์ "Borland Pascal version Turbo Vision for DOS" บน Intel 486
    Turbo Vision คือเฟรมเวิร์กหน้าต่างแบบ TUI ที่ใช้พัฒนา IDE ของ Borland Pascal และ C++
    พูดได้ว่าเป็นโหมดตัวอักษรที่เหมือนทำ JetBrains IDE ขนาด 10MB แทน 1000MB
    วิกิพีเดียของ Turbo Vision

  • LLVM เป็นเหมือนกับดักแบบหนึ่ง
    มันทำให้ bootstrap ได้เร็วมาก และได้ optimization pass สารพัดพร้อมรองรับหลายแพลตฟอร์มแบบฟรีๆ แต่ก็แลกกับการสูญเสียความสามารถในการจูนประสิทธิภาพของขั้นตอน optimization ท้ายๆ หรือขั้นตอน linking อย่างละเอียด
    คิดว่า Cranelift น่าจะถูกเปิดใช้ใน Rust เร็วๆ นี้
    อย่างไรก็ตาม Rust ก็ได้สถานะอย่างทุกวันนี้เพราะตอนเริ่มต้นเลือก LLVM
    Go ตัดสินใจมานานแล้วว่าจะไม่ฝาก code generation และการลิงก์ไว้กับภายนอก แต่จัดการเองทั้งหมด และก็ได้ประโยชน์จากการตัดสินใจนั้นเต็มๆ

    • เห็นด้วยได้ยากกับคำกล่าวที่ว่า LLVM เป็นกับดัก และ Rust นี่แหละเป็นตัวอย่างที่ดี
      ในความเป็นจริง ส่วนที่ generate code ด้วย LLVM กินสัดส่วนเล็กมากในคอมไพเลอร์ และถ้าจะเปลี่ยนก็สลับไปใช้ codegen_cranelift หรือ codegen_gcc ได้
      การผูกติดกับ SIMD vendor intrinsics นั้นเป็นปัญหาแบบ lock-in จริง แต่เป็นปัญหาจากโครงสร้างของภาษา
      สำหรับภาษาส่วนใหญ่ การเริ่มต้นด้วย LLVM backend ถือว่าสมเหตุสมผล
      ภาษาที่คล้าย C/C++ มักได้ optimization ที่ดีจาก pipeline พื้นฐานของ LLVM อยู่แล้ว แต่ยิ่งภาษามีลักษณะต่างออกไปมากเท่าไร ก็ยิ่งต้องเขียน optimization pipeline เอง
      กรณีอย่าง Go ที่รวม backend ของตัวเองมาตั้งแต่แรกก็ดูประสบความสำเร็จ แต่ไม่ได้เป็นจุดต่างพิเศษอะไร และการทำเองก็มีต้นทุนค่าเสียโอกาสไม่น้อย

    • คอมไพเลอร์ของ Go และ Ocaml เร็วมากจริงๆ
      พวกมันสร้างไลบรารีของตัวเองอย่างจริงจังมาตั้งแต่แรก และตอนนี้ก็แทบไม่มีอะไรให้เสียในแง่ความเร็วแล้ว
      ต่อไปไม่อยากทำงานในสภาพแวดล้อมที่คอมไพล์เกิน 1 นาทีอีก
      อยากให้แต่ละโปรเจ็กต์มีคอมไพเลอร์สำหรับ dev โดยเฉพาะ แล้วค่อยใช้ของหนักอย่าง llvm เฉพาะตอน final build

    • ถ้าภาษาที่อิง LLVM ตั้งเป้าแทนที่ C++ สุดท้ายก็ยังต้องพึ่ง C++ อยู่ดี
      ภาษาควร bootstrap ได้ด้วยตัวมันเอง
      ช่วงแรกอาจมีเครื่องมือสะดวกๆ ช่วยได้ แต่ก็เป็นแค่ตัวประหยัดเวลา ไม่ใช่ของจำเป็น
      เห็นด้วยอย่างยิ่งกับการตัดสินใจของทีม Go

    • หวังว่า Cranelift จะเติบโตต่อไปเรื่อยๆ
      LLVM ทุกวันนี้มี fork จากผู้ผลิต CPU แยกออกมามากมาย และแต่ละเจ้าก็เอาการปรับปรุงตาม CPU ไปมัดรวมไว้ในแพ็กเกจปิด
      ถ้าใช้ frontend ของภาษาอื่น หรือเจอบั๊กคอมไพเลอร์ สถานการณ์จะลำบากมาก

    • มีการตั้งคำถามว่าถ้าภาษาต่างๆ ย้ายไปใช้ backend อื่นได้อย่างอิสระ แล้วจะเรียกว่าเป็นกับดักได้อย่างไร

  • ถ้าความเร็วคอมไพล์รบกวนการพัฒนา ทำไมไม่สร้าง interpreter ไปเลย
    ความเร็วรันกับความเร็วคอมไพล์เป็นเรื่องที่โดยเนื้อแท้แล้วแยกจากกันได้
    ถ้าใช้ interpreter ก็ยังสร้างเครื่องมือพัฒนาเพิ่มอย่าง code instrumentation หรือการควบคุม runtime ได้ง่ายด้วย
    แม้จะมีบางกรณีน้อยมากที่ต้องดีบักเฉพาะ RELEASE binary ที่ optimize แล้ว แต่ส่วนใหญ่ใช้ interpreter หรือ DEBUG build ก็พอ

    • เคยได้ยินมาว่า Rust จัดลำดับเป็น safety, performance, usability ส่วน Zig เป็น performance, usability, safety
      ในมุมนี้ การทำให้ build เร็วขึ้นฟังดูสมเหตุสมผล แต่ interpreter เป็นทางเลือกที่เหมาะกว่าเมื่อให้ความสำคัญกับ usability ก่อน

    • ชอบแนวทางของ Julia
      ในสภาพแวดล้อมแบบ interactive เต็มรูปแบบ interpreter จะคอมไพล์โค้ดแล้วรันทันที
      สภาพแวดล้อม Common Lisp อย่าง SBCL ก็คล้ายกัน

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

    • มีคนยืนยันว่าแวดวงเกมไม่ได้เป็นกรณีพิเศษอะไร

  • จนถึงตอนนี้ ความเร็วคอมไพเลอร์ที่ดีที่สุดคือ TCC (ผลงานของ Fabrice Bellard)
    แม้ไม่มี multithread หรือ optimization ซับซ้อนก็ยังเร็วแบบทิ้งห่าง
    ถึงจะใช้ Clang สำหรับรีลีส แต่ประสิทธิภาพ code generation ของ TCC ก็ไม่ได้แย่

    • คิดว่า Delphi ให้ทั้งความสามารถในการแสดงออกที่มากกว่า ปลอดภัยกว่า และยังคอมไพล์เร็วมากด้วย

    • คอมไพเลอร์ Go ก็เร็วพอตัวเหมือนกัน

    • เคยมองว่า DMD เป็น "gold standard" เพราะคอมไพล์ได้ทั้ง C และ D แล้วยังเร็ว

    • อยากให้ TCC รองรับ C23

    • ไม่มีอะไรที่เป็น "gold standard ที่แท้จริง" เพียงหนึ่งเดียว
      vlang ก็รีคอมไพล์เสร็จในไม่กี่วินาที และคอมไพเลอร์ของ Go ก็เร็วมากเช่นกัน
      ยังมีเทคนิคอย่าง build caching ที่ช่วยหลีกเลี่ยงการรีคอมไพล์ไปเลยด้วย ดังนั้นไม่ใช่สิ่งที่ใครผูกขาดได้คนเดียว

  • ตอน build แอปด้วย zig ประทับใจกับ incremental build มาก
    สามารถ build single static binary ที่ใช้ไลบรารีหลากหลายอย่าง SQLite, luau ฯลฯ ได้เร็วพอๆ กับ Go
    แต่คอมไพเลอร์แบบ self-hosted ก็ยังมีบั๊กเหลืออยู่พอสมควร
    เช่น SQLite ยังต้องใช้ llvm และดูประเด็นที่เกี่ยวข้องได้ที่นี่

  • สงสัยว่า Zig จะทำงานร่วมกับระบบ build อย่าง Bazel หรือ Buck2 ได้ดีแค่ไหน
    สคริปต์ build ของ Zig เป็นแบบ Turing-complete เลยกังวลว่าในระบบพวกนี้การทำ caching และ build automation อาจไม่ง่าย
    คล้ายเหตุผลที่ใน Rust ไลบรารีที่ใช้ได้โดยไม่ต้องมี build.rs มักเป็นที่ชอบมากกว่า
    เลยสงสัยว่าไลบรารีของ Zig มีการทำ custom build เยอะไหม

    • สคริปต์ build ของ Zig เป็นทางเลือกทั้งหมด
      ต่อให้ไม่มี build.zig ก็ยัง build/run ไฟล์ซอร์สเดี่ยวได้ทันที
      สามารถเอา Zig ไปผูกเข้ากับ workflow ไหนก็ได้ที่เดิมใช้ GCC หรือ Clang
      อีกอย่าง Zig ยังทำหน้าที่เป็นตัวแทน C compiler ได้ด้วย
      บทความที่เกี่ยวข้อง

    • มีการแนะนำ rules_zig เป็นตัวอย่างการเชื่อม Bazel กับ Zig
      โปรเจ็กต์จริงอย่าง ZML ก็ใช้อยู่
      โปรเจ็กต์ ZML

  • สงสัยว่าการคอมไพล์และ code generation ของ Zig เทียบกับ TPDE เป็นอย่างไร
    แม้จะบอกว่าเร็วกว่า LLVM -O0 ราว 10~20 เท่า แต่ก็ดูเหมือนยังมีข้อจำกัด

  • มองว่ากลยุทธ์ของ Zig กล้าหาญมาก
    แต่ก็ยังสงสัยว่าการใช้ LLVM backend ต่อไปเป็นทางที่ถูกหรือไม่
    LLVM แข็งแกร่งทั้งด้านความเร็วคอมไพล์และการรองรับหลายแพลตฟอร์ม แต่ถ้าเป็นการสร้าง machine code ที่ดีที่สุดก็ยังไม่มีใครเทียบได้

    • ใช้ LLVM backend เฉพาะ Release build ส่วน debug build จะใช้วิธี self-hosted เป็นค่าเริ่มต้นในแพลตฟอร์มที่รองรับ
      เพราะ debug build ต้องถูกคอมไพล์ซ้ำหลายรอบในกระบวนการทดสอบจริง แนวทางนี้จึงสมเหตุสมผลกว่า

    • ไม่ค่อยอินกับความหมกมุ่นเรื่องประสิทธิภาพคอมไพเลอร์
      สุดท้ายมันคือ trade-off ระหว่าง optimization pass ต่างๆ เช่น inlining, dead code elimination ฯลฯ กับเวลาคอมไพล์
      คอมไพเลอร์ที่ไม่มี optimization จะเร็วขึ้นแบบเชิงเส้นเท่านั้น และถ้าจะ optimize มากกว่านั้น เวลาที่ใช้ก็ย่อมเพิ่มสูงอยู่เสมอ

    • เรื่อง "แอสเซมบลีที่ยอดเยี่ยม" ไม่ได้เป็นประเด็นสำคัญจริงๆ
      ตาม Proebsting's Law ความก้าวหน้าของเทคนิคคอมไพเลอร์ช้ากว่าการเพิ่มขึ้นของประสิทธิภาพเครื่องมาก
      สรุปคือ optimization ที่ง่ายและเร็วก็เพียงพอในทางปฏิบัติแล้ว
      LLVM เองก็ไม่ได้ optimize ได้แบบสัมบูรณ์ และเมื่อเทียบกับเวลาในการคอมไพล์ก็ชนเพดานได้เร็ว

  • กำลังทำ Java library ที่ห่อ C library ด้วย JNI แล้วรู้สึกว่าการ build dynamic library แยกตามแพลตฟอร์มยุ่งยากเกินไป เลยกำลังมอง Zig สำหรับการ build หลายแพลตฟอร์ม

    • ถ้าเป็นแค่ shim ง่ายๆ บน Java รุ่นใหม่ การใช้ Panama กับ jextract น่าจะดีกว่า

    • Zig ฝังทั้ง header, ซอร์สของ libc และ LLVM ของตัวเองมาให้ครบ และทำ cross compile ได้ง่ายมาก
      ง่ายชนิดที่แทบไม่ต้องคิดอะไรเลย

  • สงสัยว่าทำไม Ghostty ถึงยัง build ไม่ได้ด้วย self-hosted x86_64 backend ในตอนนี้

    • เป็นเพราะ Zig compiler crash จากบั๊ก
      ยังเป็นเทคโนโลยีใหม่จึงซับซ้อน แต่คงแก้ได้ในไม่ช้า

    • ผู้ใช้ Ghostty ส่วนใหญ่อยู่บนแพลตฟอร์ม aarch64