- ผู้เขียนเริ่มทดลองแนวทางใหม่หลังจากเรียนรู้ ภาษา Zig ไปพร้อมกับทำโปรเจ็กต์เขียนดัชนีของ AcoustID ขึ้นใหม่ และพบข้อจำกัดของงานเขียนโปรแกรมเครือข่าย
- เพื่อให้ได้ โมเดล asynchronous I/O และ concurrency แบบที่เคยใช้ใน C++ และ Go บน Zig ด้วย ผู้เขียนจึงตัดสินใจพัฒนาไลบรารีขึ้นเอง
- ผลลัพธ์คือ ไลบรารี Zio ที่นำโมเดล concurrency สไตล์ Go มาปรับใช้กับ Zig ทำให้เขียนโค้ด async ที่ดูเหมือน synchronous ได้โดยไม่ต้องใช้ callback
- Zio รองรับ asynchronous network/file I/O, channel, synchronization primitives, signal watching และให้ประสิทธิภาพในโหมด single-thread เร็วกว่า Go หรือ Tokio ของ Rust
- โปรเจ็กต์นี้แสดงให้เห็นถึง ความเป็นไปได้ในการผสานประสิทธิภาพระดับระบบของ Zig เข้ากับโมเดล concurrency สมัยใหม่ และถูกมองว่าเป็นจุดเปลี่ยนสำคัญของการขยายระบบนิเวศ Zig
ภาษา Zig และแรงจูงใจช่วงแรก
- เดิมทีผู้เขียนเฝ้าดู Zig ซึ่งถูกออกแบบมาเป็น ภาษาระดับล่างสำหรับซอฟต์แวร์เสียง อยู่แล้ว แต่ยังไม่รู้สึกว่าจำเป็นต้องใช้จริง
- เขาเริ่มสนใจหลังเห็นกรณีที่ Andrew Kelley ผู้สร้าง Zig นำอัลกอริทึม Chromaprint ของผู้เขียนไปเขียนใหม่ด้วย Zig
- ผู้เขียนใช้โปรเจ็กต์ เขียน reverse index ของ AcoustID ขึ้นใหม่ เป็นโอกาสในการเรียนรู้ Zig และสุดท้ายก็ได้งานพัฒนาที่ เร็วกว่าและขยายระบบได้ดีกว่าเวอร์ชัน C++
- แต่เมื่อถึงขั้นเพิ่มอินเทอร์เฟซฝั่งเซิร์ฟเวอร์ ก็พบปัญหา การรองรับ asynchronous networking ที่ยังไม่เพียงพอ
แนวทางเดิมและข้อจำกัด
- ในเวอร์ชัน C++ ก่อนหน้า ผู้เขียนใช้ เฟรมเวิร์ก Qt เพื่อจัดการ asynchronous I/O แม้จะเป็นแบบ callback-based แต่ก็ใช้งานได้เพราะมีฟังก์ชันรองรับครบถ้วน
- ต่อมาในต้นแบบ ผู้เขียนใช้ ความสะดวกของ networking และ concurrency ในภาษา Go แต่ใน Zig ยังไม่มี abstraction ระดับใกล้เคียงกัน
- หากจะสร้าง TCP server และชั้นคลัสเตอร์ ด้วย Zig จะเกิดความไม่มีประสิทธิภาพจากการต้องสร้างหลายเธรดจำนวนมาก
- เพื่อแก้ปัญหานี้ ผู้เขียนจึงลงมือเขียน ไคลเอนต์ Zig สำหรับระบบส่งข้อความ NATS (
nats.zig) เอง และได้สำรวจความสามารถด้าน networking ของ Zig อย่างลึกซึ้ง
การมาของไลบรารี Zio
- จากประสบการณ์เหล่านี้ ผู้เขียนจึงเผยแพร่ Zio: ไลบรารี asynchronous I/O และ concurrency สำหรับ Zig
- Zio มีเป้าหมายเพื่อให้ เขียนโค้ด async ได้โดยไม่ต้องพึ่ง callback โดยภายในยังคงทำงานแบบ asynchronous I/O แต่โครงสร้างภายนอกจะดูเหมือน synchronous
- มีการนำ โมเดล concurrency สไตล์ Go มาปรับใช้กับ Zig ในรูปแบบที่มีข้อจำกัดบางส่วน
- task ของ Zio อยู่ในรูปแบบ stackful coroutine ที่มี fixed-size stack
- เมื่อเรียก
stream.read() งาน I/O จะทำงานอยู่เบื้องหลัง และเมื่อเสร็จแล้ว task จะถูก resume เพื่อคืนผลลัพธ์
- วิธีนี้ช่วยทั้ง ลดความซับซ้อนของการจัดการสถานะ และ เพิ่มความอ่านง่ายของโค้ด
องค์ประกอบฟังก์ชันและโครงสร้างรันไทม์
- Zio รองรับ asynchronous network และ file I/O แบบครบถ้วน, synchronization primitives (mutex, condition variable ฯลฯ), channel สไตล์ Go, การเฝ้าดูสัญญาณจาก OS เป็นต้น
- task สามารถทำงานได้ทั้งใน โหมด single-thread และ multi-thread
- ในโหมด multi-thread task สามารถย้ายข้ามเธรดได้ ช่วย ลด latency และเพิ่มสมดุลของโหลด
- มีการรองรับ อินเทอร์เฟซ Reader/Writer มาตรฐาน เพื่อให้เข้ากันได้กับไลบรารีภายนอก
ประสิทธิภาพและการเปรียบเทียบ
- ผู้เขียนยังไม่ได้เผยแพร่ benchmark อย่างเป็นทางการ แต่ระบุว่าได้ยืนยันแล้วว่า ในโหมด single-thread เร็วกว่า Go และ Tokio ของ Rust
- ต้นทุนของ context switching ต่ำถึงระดับการเรียกฟังก์ชัน ทำให้ความเร็วในการสลับแทบใกล้เคียงกับไม่มีต้นทุน
- โหมด multi-thread ยังไม่แข็งแกร่งเท่า Go/Tokio แต่ก็ให้ ประสิทธิภาพใกล้เคียงกันหรือเร็วกว่าเล็กน้อย
- ในอนาคต หากเพิ่มฟังก์ชัน fairness ก็อาจทำให้ประสิทธิภาพลดลงบางส่วน
โค้ดตัวอย่างและการใช้งาน
- ในเอกสารมี โค้ดตัวอย่าง HTTP server ที่สร้างบน Zio รวมอยู่ด้วย
- ใช้
zio.net.Stream เพื่อรับการเชื่อมต่อ และจัดการแต่ละการเชื่อมต่อใน task แยกกัน
zio.Runtime ทำหน้าที่ดูแลการรัน task และการจัดตาราง I/O
- โครงสร้างนี้ช่วยให้ เขียน asynchronous I/O ได้เหมือนโค้ด synchronous พร้อมทั้งทำให้ ควบคุมลำดับการทำงานและการคืนทรัพยากรได้อย่างชัดเจน
แผนต่อไปและความสำคัญ
- ผู้เขียนมองว่า Zio แสดงให้เห็นว่า Zig ไม่ได้เป็นเพียง ภาษาสำหรับโค้ดระบบประสิทธิภาพสูง เท่านั้น แต่ยังพัฒนาไปเป็น ภาษาสำหรับสร้างแอปพลิเคชันเครือข่ายอย่างสมบูรณ์ ได้ด้วย
- ขั้นถัดไปคือการ เขียนไคลเอนต์ NATS ใหม่บน Zio และมีแผนพัฒนา ไลบรารี HTTP client/server ที่อิงกับ Zio
- โปรเจ็กต์นี้ถูกมองว่าเป็นความพยายามสำคัญในการ ขยายโครงสร้างพื้นฐานด้าน networking และ concurrency ของระบบนิเวศ Zig และเป็นความพยายามสร้าง โมเดลรันไทม์สมัยใหม่ที่เทียบชั้นกับ Go หรือ Rust
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
setsockoptZig มีเลเยอร์ POSIX API ให้ใช้งาน อ้างอิง: เอกสาร setsockoptasyncio.timeoutของ Python โค้ดตัวอย่าง: