1 คะแนน โดย GN⁺ 2024-09-02 | 1 ความคิดเห็น | แชร์ทาง WhatsApp

การเพิ่มประสิทธิภาพขนาดไบนารีของไลบรารี {fmt}

  • แนะนำไลบรารี {fmt}

    • {fmt} เป็นไลบรารีสำหรับการจัดรูปแบบที่ขึ้นชื่อเรื่องขนาดไบนารีที่เล็ก
    • เมื่อเทียบกับ IOStreams, Boost Format, tinyformat และอื่น ๆ แล้ว ขนาดโค้ดต่อการเรียกใช้ฟังก์ชันเล็กกว่ามาก
    • ลดภาระจากเทมเพลตให้เหลือน้อยที่สุดผ่าน type erasure
  • การจัดรูปแบบผ่าน type erasure

    • ฟังก์ชัน format มอบหมายงานให้ฟังก์ชัน vformat
    • output iterator และ output type อื่น ๆ ก็ถูกทำเป็น type erasure ผ่าน buffer API ที่ออกแบบมาโดยเฉพาะ
    • ลดการใช้เทมเพลตให้ต่ำที่สุดเพื่อลดขนาดไบนารีและเวลาในการบิลด์
  • โค้ดตัวอย่าง

    #include <fmt/base.h>
    int main() { fmt::print("The answer is {}.", 42); }
    
    • โค้ดข้างต้นคอมไพล์ออกมาได้ขนาดเล็กกว่าโค้ด IOStreams มาก
    • แม้เทียบกับ printf ก็มีขนาดใกล้เคียงกัน พร้อมให้ความปลอดภัยของชนิดข้อมูลขณะรันไทม์
  • การปรับขนาดไบนารีให้เหมาะสม

    • ในปี 2020 มีการลดขนาดของไลบรารีให้ต่ำกว่า 100kB
    • ขนาดไบนารีของเวอร์ชันล่าสุด (11.0.2) อยู่ที่ 75kB
    • หากปิดการรองรับ locale จะลดขนาดลงเหลือ 71kB ได้
  • การวิเคราะห์ด้วยเครื่องมือ Bloaty

    • การจัดรูปแบบตัวเลข โดยเฉพาะตัวเลขทศนิยมแบบ floating-point กินพื้นที่ส่วนใหญ่
    • หากไม่จำเป็นต้องรองรับ floating-point ก็สามารถปิดส่วนนี้ได้
  • การเพิ่มประสิทธิภาพการจัดรูปแบบตามชนิดข้อมูล

    • ตั้งค่าแมโคร FMT_BUILTIN_TYPES เป็น 0 เพื่อให้จัดการเฉพาะชนิด int แบบพิเศษ และให้ชนิดอื่นจัดการผ่าน extended API
    • วิธีนี้ช่วยลดขนาดไบนารีลงเหลือ 31kB
  • การลบอาร์ติแฟกต์จาก locale

    • ใช้แมโคร FMT_USE_LOCALE เพื่อลบอาร์ติแฟกต์จาก locale จะลดขนาดลงเหลือ 27kB
  • จุดแลกเปลี่ยนระหว่างความเร็วกับขนาด

    • หากใช้แมโคร FMT_OPTIMIZE_SIZE เพื่อเน้นการลดขนาด จะลดขนาดไบนารีลงเหลือ 23kB
  • การตัดการพึ่งพา C++ standard library

    • ปิด exception และใช้ตัวเลือก -nodefaultlibs เพื่อตัดการพึ่งพา C++ runtime
    • นำ custom allocator ที่ใช้ malloc และ free มาใช้เพื่อลดขนาดไบนารีลงเหลือ 14kB
  • การตรวจสอบผลลัพธ์

    • ใช้คำสั่ง ldd เพื่อตรวจสอบว่าได้ตัดการพึ่งพา C++ runtime ออกแล้ว

สรุปโดย GN⁺

  • ไลบรารี {fmt} เป็นไลบรารีจัดรูปแบบที่ให้ทั้งขนาดไบนารีเล็กและความปลอดภัยของชนิดข้อมูลขณะรันไทม์
  • สามารถลดขนาดไบนารีได้อย่างมากผ่าน type erasure และการตั้งค่าแมโคร
  • เมื่อตัดการพึ่งพา C++ standard library ออก ก็สามารถใช้งานได้อย่างมีประสิทธิภาพในระบบฝังตัวด้วย
  • ไลบรารีที่มีความสามารถคล้ายกัน ได้แก่ IOStreams, Boost Format และ tinyformat

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

 
GN⁺ 2024-09-02
ความคิดเห็นจาก Hacker News
  • โดยพื้นฐานแล้ว {fmt} ไม่ขึ้นกับ locale
  • การจัดรูปแบบเลขทศนิยมแบบ floating-point ต้องใช้โค้ดจำนวนมาก
    • โปรเจกต์ Dragonbox มีโค้ดที่ปรับแต่งมาอย่างดีและน่าอ่าน
  • ตัวจัดสรรหน่วยความจำเริ่มต้นของ C++ ไม่ได้ใช้ malloc และ free
    • สงสัยว่าทำไมตัวจัดสรรหน่วยความจำเริ่มต้นของ libc++ ถึงไม่เรียก malloc และ free ของ libc
  • มีโปรเจกต์ที่ทำให้ printf(Hello, World!\n") สร้างไฟล์ executable ขนาด 1008 ไบต์ได้
    • เทียบกันตรง ๆ ได้ยาก แต่ก็น่าสนใจในฐานะข้อมูลอ้างอิง
  • บนระบบที่โปรแกรม C ซึ่งมีฟังก์ชัน main ว่าง ๆ มีขนาด 6kB นั้น {fmt} เพิ่มขนาดไบนารีไม่ถึง 10kB
    • เป็นการทดสอบที่น่าสนใจ
  • เคยคาดหวังว่าไลบรารีจัดรูปแบบขนาดเล็กจะต้องใช้เพียงราว 50 ไบต์สำหรับการแสดงผลสตริงและจำนวนเต็ม
    • สตริงประกอบด้วยคำสั่งประมาณ 4 คำสั่ง
    • จำนวนเต็มประกอบด้วยคำสั่งประมาณ 20 คำสั่ง
    • floating-point ไม่ได้ถูกใช้ในหลายโปรแกรม จึงควรคอมไพล์เฉพาะเมื่อจำเป็น
    • ถ้าพื้นที่โค้ดของไมโครคอนโทรลเลอร์มีแค่ 2 กิโลไบต์ ก็จะไม่ใส่ไลบรารีจัดรูปแบบสตริงขนาด 14 กิโลไบต์เข้าไป
  • การปรับแต่งที่หลุดออกจากกรอบความคิดแบบนี้สนุกมาก
  • ใช้เวลาสักพักกว่าจะรู้ว่า "14k" หมายถึง "14kB"
  • fmt มักก่อปัญหาอยู่เสมอ
    • ใน .NET ก็เกิดปัญหาแบบเดียวกัน
    • ถ้าต้องจัดการการจัดรูปแบบ/แยกวิเคราะห์ตัวเลขมาก ๆ ลิงเกอร์จะดึงโค้ดที่เกี่ยวกับ floating-point และ BigInt เข้ามาจำนวนมาก ทำให้ขนาดไบนารีใหญ่ขึ้น