ประกาศ Zstandard ที่พัฒนาใน Rust
(trifectatech.org)- libzstd-rs-sys เป็นโปรเจกต์บีบอัดลำดับที่สามของมูลนิธิ Trifecta ต่อจาก zlib·bzip2 และเป็นรีลีสแรกของ zstd ที่พัฒนาบน Rust
- Zstd เป็นฟอร์แมตการบีบอัดที่ออกแบบมาสำหรับ CPU สมัยใหม่ มีทั้งความเร็วมากกว่า gzip และอัตราการบีบอัดที่ดีกว่า จึงคาดว่าจะค่อย ๆ เข้ามาแทนที่ gzip ในทราฟฟิกเว็บ
- zstd crate บน Rust ที่มีอยู่เดิมคอมไพล์โค้ด C จากซอร์ส จึงต้องใช้ C toolchain และการรองรับเป้าหมาย ทำให้การตั้งค่าบน Windows·WebAssembly อาจยุ่งยาก
- อิมพลีเมนเทชัน Rust สามารถคอมไพล์เป็น ไลบรารี C ที่เข้ากันได้แบบ drop-in และกำลังตรวจสอบทางเลือกแทน C reference implementation ด้วย test suite·fuzz testing·Miri
- การคลายการบีบอัดแบบพื้นฐานช้ากว่า C อยู่ไม่กี่เปอร์เซ็นต์ แต่การลดลงราว 3% เป็นต้นทุนของ memory safety และสามารถใช้แฟล็กทดลองเพื่อให้ได้ประสิทธิภาพเทียบเท่า C
รีลีสแรกและความหมายของการพัฒนาด้วย Rust
- Trifecta Tech Foundation ประกาศรีลีสแรกของ libzstd-rs-sys ซึ่งดูแล zstd เป็นโปรเจกต์บีบอัดลำดับที่สามต่อจาก zlib และ bzip2
- Zstd เป็นฟอร์แมตการบีบอัดที่ออกแบบโดยคำนึงถึง CPU สมัยใหม่ ทำงานได้เร็วกว่า gzip มากและให้อัตราการบีบอัดที่ดีกว่า
- zstd ถูกใช้งานอย่างแพร่หลายอยู่แล้ว และคาดว่าจะค่อย ๆ เข้ามาแทนที่ gzip ในทราฟฟิกเว็บ
- ใน Rust มีการใช้งาน zstd ได้อยู่แล้วผ่าน crate zstd แต่ crate เดิมคอมไพล์โค้ด C จากซอร์ส จึงต้องใช้ C toolchain และการรองรับสำหรับเป้าหมาย
- การตั้งค่า C toolchain สำหรับ Windows หรือ WebAssembly อาจทำได้ยาก ดังนั้นอิมพลีเมนเทชัน Rust ล้วนจึงมอบประสบการณ์การใช้ dependency ที่ดีกว่าสำหรับนักพัฒนา Rust
libzstd-rs-sysสามารถคอมไพล์เป็น ไลบรารี C ที่เข้ากันได้แบบ drop-in ได้เช่นเดียวกับงานของ zlib·bzip2 และมีเป้าหมายเป็นทางเลือกแทน C reference implementation- C reference implementation ได้รับการดูแลโดย Meta และการมีส่วนร่วมต้องทำข้อตกลง Contributor Agreement กับ Meta ดังนั้นการมีอิมพลีเมนเทชันที่เป็นอิสระ มีประสิทธิภาพดี และเข้ากันได้ จึงช่วยเสริมความแข็งแกร่งให้ระบบนิเวศโอเพนซอร์สได้
การตรวจสอบ ประสิทธิภาพ และงานที่เหลือ
- reference implementation ระยะแรกถูกแปลงด้วย
c2rustและหลังจากนั้นได้จัดระเบียบส่วนการคลายการบีบอัดและ dictionary builder เสร็จสิ้นแล้ว - โค้ด Rust ถูกคอมไพล์เป็นไลบรารีสแตติกของ C แล้วตรวจสอบด้วย test suite ของ reference implementation
- ยังใช้ fuzz testing และ Miri เพื่อตรวจสอบความถูกต้องของอิมพลีเมนเทชัน
- พรีรีลีสมีให้ที่ libzstd-rs-sys v0.0.1-prerelease.2
-
ต้นทุนของ memory safety
- ประสิทธิภาพการคลายการบีบอัดแบบพื้นฐานช้ากว่า C reference implementation อยู่ไม่กี่เปอร์เซ็นต์
- ทุกการเปลี่ยนแปลงที่ merge เข้าสู่
mainจะถูกวัดด้วย benchmark suite - หากเปิดใช้ feature flag
unsafe-performance-experimentalจะได้ประสิทธิภาพ เทียบเท่า C - แฟล็กนี้จะปิดการตรวจสอบขอบเขตใน 4 จุดที่ใช้ข้อมูลอินพุตไปทำ indexing ของโครงสร้างข้อมูล
- สำหรับผู้ใช้ส่วนใหญ่ การลดลงของประสิทธิภาพราว 3% น่าจะเป็นต้นทุนที่ยอมรับได้เพื่อแลกกับ memory safety ที่ดีขึ้น
- หากต้องการรีดประสิทธิภาพขั้นสุดท้าย ก็สามารถยอมรับความเสี่ยงแล้วเปิดแฟล็กนี้ได้ โดยพฤติกรรมใน 4 จุดดังกล่าวจะสอดคล้องกับ C ที่ไม่มีการตรวจสอบขอบเขต
-
การพัฒนา compression และการผสานเข้ากับระบบนิเวศ
- ส่วนของ compression ยังอยู่ระหว่างการหาเงินทุน
- มีการแชร์โค้ดระหว่าง compression และ decompression จึงได้ตรวจดูโค้ดฝั่ง compression ไปบ้างแล้ว แต่การจัดระเบียบส่วนใหญ่ยังคงเหลืออยู่
- มีการตั้ง benchmark เพื่อป้องกันประสิทธิภาพของ compression ถดถอย และกำลังตรวจสอบว่าผลลัพธ์ที่สร้างขึ้นถูกต้องหรือไม่ด้วย test suite ของ reference implementation
- งานที่เหลือสรุปไว้ใน Milestone 4: Encoder implementation
- มี
zstdfork ที่ใช้libzstd-rs-sysแทนไลบรารี C และต้องการผลักดันให้ถูกรวมเข้าต้นน้ำในอนาคต - สำหรับ API ที่ถูกใช้งานมากที่สุด การผสานรวมค่อนข้างตรงไปตรงมา
- ในฟีเจอร์
experimentalมีความไม่สอดคล้องกันตรงที่zstd-safeใช้ enum แต่เพื่อความปลอดภัยของ FFI จำเป็นต้องใช้struct
-
การสนับสนุน
- งานด้าน decompression ได้รับเงินทุนจาก Chainguard, Astral, NLnet Foundation
- Sovereign Tech Agency ลงทุนใน dictionary builder
1 ความคิดเห็น
ความคิดเห็นจาก Lobste.rs
ข่าวนี้น่ายินดีมาก เมื่อไม่กี่วันก่อนผมต้องดึง
libc-devเข้ามาเพื่อจะ build zstd เพราะมี dependency ตัวหนึ่ง เลยสงสัยว่ามีใครเคยลอง reimplement แบบจริงจังด้วย Rust บ้างไหมหวังว่าจะถูกชุมชนนำไปใช้อย่างแพร่หลาย
ผมกำลังทำโปรเจ็กต์ที่อิงกับ WireGuard ด้วย Rust เลยดึงไลบรารีเข้ารหัสของ Rust มาหลายตัว ข้อดีเรื่อง memory safety นั้นชัดเจน แต่ต่างจากไลบรารี C เก่าแก่ตรงที่ไม่ได้ผ่าน security audit กันทั้งหมด
สุดท้ายสิ่งที่ผมสงสัยคือ การเขียนอัลกอริทึมเหล่านี้ใหม่ด้วย Rust คุ้มกับต้นทุนที่ต้องจ่ายหรือไม่
โค้ดที่ “น่าเบื่อ” และไม่ใช่ส่วนเข้ารหัสอย่าง parsing, protocol state, buffer management ต้องทำงานให้ถูกต้อง ระบบถึงจะปลอดภัย ถ้าผู้โจมตีส่งแพ็กเก็ตวิเศษที่ทำให้รันโค้ดตามอำเภอใจได้ พวกเขาก็ไม่จำเป็นต้องพึ่งการวิเคราะห์คริปโตขั้นสูงหรือ timing side-channel ที่ลดเวลาถอดรหัสจากหลักพันปีเหลือหลักสิบปี
ทั้งสองฝั่งมี memory safety และมีข้อดีตรงที่ต้องมีเพียงขอบเขต unsafe FFI ที่แคบมากเท่านั้น ตอนนี้ไลบรารีเข้ารหัสของ Go มีความสุกงอมมากกว่าสิ่งที่ Rust หรือพูดให้ชัดคือ ecosystem บน crates.io มีให้
มีใครรู้แหล่งอธิบายที่จัดทำเป็นเอกสารไหมว่าเมื่อไรควรใช้ suffix
-sysกับ-rs-sys? ตามสัญชาตญาณผมคิดว่า-sysใช้กับ crate ที่ครอบ system library ซึ่งไม่ได้เขียนด้วย Rustแต่กรอบคิดนั้นทำให้
-rs-sysดูไม่ค่อยสมเหตุสมผล เลยเดาว่าสัญชาตญาณของผมน่าจะผิด มีใครรู้แหล่งอ้างอิงที่เชื่อถือได้ไหม?*-sys: เป็นธรรมเนียมเก่าและใช้กันแพร่หลาย อธิบายไว้ที่ https://doc.rust-lang.org/cargo/reference/… แต่ไม่เข้ากับ crate นี้เลยแม้แต่น้อยlib*: เรารู้อยู่แล้วว่านี่คือ library และใน Rust ก็ไม่ใช่ prefix ตามธรรมเนียม อีกทั้งลิงก์ข้างบนก็ชี้แบบเกือบตรง ๆ ว่าควรหลีกเลี่ยงควบคู่กับ*-sysถ้าเป็นlibzstd-sysคนอาจคาดว่าจะลิงก์กับliblibzstdและขอเสริมว่าแม้นอกโลก Rust ผมก็เคยเห็นชื่อlibซ้อนสองชั้นจริง ๆ*-rs: ตามที่ https://rust-lang.github.io/api-guidelines/naming.html บอกไว้ว่า “ทุก crate ก็คือ Rust อยู่แล้ว! ไม่จำเป็นต้องคอยย้ำเตือนผู้ใช้ตลอดเวลา”-sysจะเปิดเผย อินเทอร์เฟซแบบ C ที่ดิบมาก มีโค้ด unsafe อยู่ข้างในเยอะ และมัก build หรือลิงก์กับไลบรารีภายนอกส่วนชื่อ
-rs-sysผมเห็นว่าถูกใช้อย่างไม่ค่อยสม่ำเสมอ ดูเหมือนจะใช้กับไลบรารีที่ build โค้ดภายนอกซึ่งถูกห่อไว้อีกชั้นใน Rust crate เช่น Rust implementation ที่ยังทำไม่เสร็จและยังมีส่วน C เหลืออยู่ หรือ Rust support code สำหรับ sys cratelibzstdคือชื่อดั้งเดิม ไลบรารี C มักมีlibอยู่ในชื่อ และเขาก็เก็บชื่อนั้นไว้เพื่อการอ้างอิงแทนที่จะเปลี่ยนให้เข้ากับธรรมเนียมของ Rust/Cargo-rsใช้บอกว่าเป็น Rust rewrite เพื่อแยกจาก C implementation ของ Facebook เป็น suffix ทั่วไปที่หลายโปรเจ็กต์ Rust ใช้กัน คล้ายกับที่ไลบรารี Python ตั้งชื่อแบบpysomething-sysมีไว้เพราะ implementation นี้เป็น drop-in replacement ที่เปิดเผย unsafe C API จากมุมมองของผู้ใช้ Cargo มันไม่ใช่ไลบรารี Rust ไม่มี Rust interface และถูกเรียกใช้เหมือน C code ที่เป็น C functionดังนั้นมันจึงไม่ใช่
-rs-sysแต่เป็นเวอร์ชัน-sysของlibzstd-rsทำไมต้องเลือกตัวนี้แทน ruzstd? หรือว่าควรไปลงทุนกับ crate ที่มีอยู่เดิมมากกว่า?
การ port แบบ 1:1 ทำให้สามารถได้ทั้งความเร็วใกล้เคียงและความสามารถเทียบเท่า ผ่านการแปลงโค้ดที่ค่อนข้างตรงไปตรงมา แทนที่จะต้องกลับไปหาคำตอบใหม่ว่าจะสร้างตัวบีบอัดที่เร็วและครบถ้วนเท่ากันบนอีก codebase หนึ่งได้อย่างไร
“reference implementation ฝั่ง C ถูกดูแลโดย Meta และถ้าจะร่วม contribute ต้องเซ็น contributor agreement กับ Meta”
ผมเพิ่งรู้ข้อเท็จจริงที่น่าสนใจเมื่อไม่นานมานี้ว่า zstd implementation อ้างอิงที่ Facebook ดูแลอยู่นั้นก็ใช้ LLM เขียนโค้ดเช่นกัน และยังเป็น dependency ของ openssl ด้วย ดังนั้นผมสนับสนุนเต็มที่ถ้าจะมีทางเลือกเพิ่มขึ้น
“ในค่าตั้งต้น ประสิทธิภาพการคลายการบีบอัดของ implementation เราช้ากว่า C reference implementation อยู่ไม่กี่เปอร์เซ็นต์”
แค่นี้ก็เพียงพอแล้วสำหรับสิ่งที่ต้องรู้เกี่ยวกับโปรเจ็กต์นี้
เพื่ออ้างอิง เนื้อหาถัดจากประโยคที่คุณยกมากล่าวต่อแบบนี้
“อย่างไรก็ตาม ถ้าเปิด feature flag
unsafe-performance-experimentalก็จะได้ประสิทธิภาพเท่ากับ C ดังนั้นผมคิดว่าการลดลงของประสิทธิภาพระดับนี้ถือว่าสมเหตุสมผล feature flag นี้จะปิด boundary check 4 จุดที่ใช้ข้อมูลอินพุตไปทำดัชนีโครงสร้างข้อมูล สำหรับผู้ใช้ส่วนใหญ่ การสูญเสียประสิทธิภาพราว 3% น่าจะเป็นต้นทุนที่ยอมรับได้เพื่อแลกกับ memory safety ที่ดีขึ้น หากต้องการรีดประสิทธิภาพขั้นสุดท้ายจริง ๆ ก็สามารถเปิด flag นี้ได้โดยรับความเสี่ยงเอง พฤติกรรมใน 4 จุดนี้จะเหมือนกับ C ที่ไม่ทำ boundary check และดูเหมือนว่าจะทำงานได้โดยไม่มีปัญหาใน production system จำนวนมาก”