• bzip2 crate 0.6.0 เลือกใช้ libbz2-rs-sys ซึ่งเป็นการใช้งานอัลกอริทึม bzip2 ด้วย Rust เป็นค่าเริ่มต้น ช่วยตัดการพึ่งพา C ออก และปรับปรุงทั้งความเร็วกับความสะดวกในการ cross-compile
  • bzip2 เป็นอัลกอริทึมเก่าที่มีการใช้งานลดลง แต่ โปรโตคอลและไลบรารี หลายตัวยังต้องรองรับเพื่อให้เป็นไปตามสเปก จึงยังคงอยู่ลึกใน dependency tree
  • การใช้งานด้วย Rust ใช้ CPU cycles น้อยกว่า C ประมาณ 9.66~14.87% ในการบีบอัด และในการแตกไฟล์ก็ปรับปรุงขึ้น 4.48~10.00% ตลอดชุดทดสอบ
  • การตัด C ออกทำให้การ build สำหรับ WebAssembly, Windows, Android ง่ายขึ้น และโดยค่าเริ่มต้นจะไม่ส่งออก symbol ของ libbz2-rs-sys จึงลดความเสี่ยงของ symbol collision กับ dependency อื่น
  • การ audit พบและแก้บั๊กเชิงตรรกะแบบ off-by-one 1 รายการ รวมถึงปรับข้อจำกัดบางส่วนของ fuzzer และทำให้ไลบรารีกับแอปพลิเคชันระดับบนที่ใช้ bzip2 สามารถรัน MIRI ได้ด้วย

การเปลี่ยน implementation เริ่มต้นของ bzip2 0.6.0

  • bzip2 crate 0.6.0 ใช้ libbz2-rs-sys ซึ่งเป็นการใช้งานอัลกอริทึม bzip2 ด้วย Rust เป็นค่าเริ่มต้น
  • เมื่อตัด dependency C เดิมออก bzip2 crate จึงเร็วขึ้นและ cross-compile ได้ง่ายขึ้น
  • libbz2-rs-sys crate สามารถ build เป็น ไลบรารี dynamic ของ C ได้ เพื่อให้โปรเจกต์ C ก็ใช้ประโยชน์จากการปรับปรุงนี้ได้
  • แม้ bzip2 จะไม่ค่อยถูกใช้งานมากในปัจจุบัน แต่โปรโตคอลและไลบรารีจำนวนมากยังต้องรองรับเพื่อให้เป็นไปตามสเปก และยังคงอยู่ลึกใน dependency tree ของหลายโปรเจกต์
  • รายละเอียดการ implement ดูได้ในบทความก่อนหน้า Translating bzip2 with c2rust

ผลลัพธ์ด้านประสิทธิภาพที่ดีขึ้น

  • โดยทั่วไป implementation ด้วย Rust เร็วกว่า implementation ด้วย C และในบางกรณีก็อยู่ในระดับใกล้เคียงกับประสิทธิภาพของ C
    • เท่าที่ทราบ ไม่มีกรณีที่ช้ากว่าอย่างมีนัยสำคัญ
  • ใน benchmark การบีบอัด CPU cycles ลดลงเมื่อเทียบกับ C
    • sample3.ref level 1: 38.51M33.53M, -14.87%
    • silesia-small.tar level 1: 3.43G3.00G, -14.30%
    • silesia-small.tar level 9: 3.47G3.17G, -9.66%
    • level ของ bzip2 หมายถึงปริมาณหน่วยความจำใช้งาน และไม่มีผลมากนักต่อประสิทธิภาพ
    • sample3.ref จัดสรรหน่วยความจำมากกว่าขนาดไฟล์ตั้งแต่ level 1 แล้ว ดังนั้น level ที่สูงกว่าจึงเกี่ยวข้องน้อย
  • ประสิทธิภาพการแตกไฟล์ก็ดีขึ้นตลอดชุดทดสอบ
    • sample3.bz2: -4.48%
    • sample1.bz2: -8.63%
    • sample2.bz2: -7.67%
    • dancing-color.ps.bz2: -5.17%
    • re2-exhaustive.txt.bz2: -7.65%
    • zip64support.tar.bz2: -10.00%
  • บนเครื่อง benchmark ที่ใช้ macOS บางครั้งตัวเลขประสิทธิภาพการแตกไฟล์ออกมาต่ำ
    • ยังไม่ทราบสาเหตุ และบน macOS การทำให้เครื่องมือติดตามประสิทธิภาพแบบ perf ทำงานอัตโนมัติทำได้ยาก

การ build และการลดปัญหา symbol collision

  • การ cross-compile โปรเจกต์ Rust ที่มี dependency C มักทำงานได้ทันทีด้วย cc crate แต่ถ้าล้มเหลว การ debug ข้อผิดพลาดอาจทำได้ยาก
    • การ link กับไลบรารีของระบบก็อาจสร้างปัญหาที่สับสนและทำซ้ำได้ยาก
    • การ compile bzip2 เป็น WebAssembly มีปัญหามานานแล้ว
    • เมื่อตัด dependency C ออกและใช้เฉพาะโค้ด Rust การ build สำหรับ WebAssembly, Windows, Android จึงทำงานได้ง่ายขึ้น
  • โดยค่าเริ่มต้น libbz2-rs-sys จะไม่ export symbol
    • หากใช้ dependency C ต้อง export symbol เพื่อให้บล็อก extern ของ Rust หาเจอ
    • ชื่อที่ export อาจชนกันเมื่อ dependency อื่นประกาศ symbol เดียวกัน
    • หากโปรเจกต์ Rust ต้องการ export symbol ก็เปิดใช้ได้ด้วย feature flag

ผลการตรวจสอบและ audit

  • implementation ของ bzip2 ที่มีประสิทธิภาพจำเป็นต้องมี unsafe code บางส่วน และการจำลอง interface ของ C ด้วย Rust ต้องใช้ unsafe code มากขึ้น
    • โค้ดดังกล่าวสามารถรันใน MIRI ได้
    • ไลบรารีระดับสูงหรือแอปพลิเคชันที่ใช้ bzip2 ตอนนี้ก็สามารถรันด้วย MIRI ได้เช่นกัน
  • การ audit พบ บั๊กเชิงตรรกะแบบ off-by-one 1 รายการ และแก้ข้อจำกัดบางส่วนของ fuzzer
    • นอกเหนือจากนั้นไม่มีประเด็นสำคัญที่พบ
    • การ audit ดำเนินการโดย Radically Open Security และรายงานฉบับเต็มดูได้ที่ รายงาน audit PDF
  • งานครั้งนี้ได้รับความช่วยเหลือจาก Alex Crichton ผู้ดูแล bzip2 crate, Radically Open Security ที่ให้การ audit และความเชี่ยวชาญ, และ NLnet Foundation ซึ่งให้ทุนสนับสนุนผ่าน e-Commons Fund

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น