1 điểm bởi GN⁺ 2024-09-02 | 1 bình luận | Chia sẻ qua WhatsApp

Tối ưu kích thước nhị phân của thư viện {fmt}

  • Giới thiệu về thư viện {fmt}

    • {fmt} là một thư viện định dạng nổi tiếng với kích thước nhị phân nhỏ
    • So với IOStreams, Boost Format, tinyformat, v.v., kích thước mã trên mỗi lần gọi hàm nhỏ hơn nhiều
    • Giảm thiểu gánh nặng template thông qua type erasure
  • Định dạng thông qua type erasure

    • Hàm format ủy quyền công việc cho hàm vformat
    • Iterator đầu ra và các kiểu đầu ra khác cũng được type erasure thông qua API buffer được thiết kế riêng
    • Giảm thiểu việc sử dụng template để giảm kích thước nhị phân và thời gian build
  • Mã ví dụ

    #include <fmt/base.h>
    int main() { fmt::print("The answer is {}.", 42); }
    
    • Đoạn mã trên được biên dịch với kích thước nhỏ hơn nhiều so với mã IOStreams
    • So với printf, kích thước cũng tương đương nhưng cung cấp type safety ở runtime
  • Tối ưu kích thước nhị phân

    • Năm 2020, tác giả đã thực hiện công việc giảm kích thước thư viện xuống dưới 100kB
    • Kích thước nhị phân của phiên bản mới nhất (11.0.2) là 75kB
    • Có thể giảm xuống 71kB nếu tắt hỗ trợ locale
  • Phân tích bằng công cụ Bloaty

    • Định dạng số, đặc biệt là định dạng số dấu phẩy động, chiếm phần lớn dung lượng
    • Nếu không cần hỗ trợ số dấu phẩy động thì có thể vô hiệu hóa phần này
  • Tối ưu định dạng theo từng kiểu

    • Đặt macro FMT_BUILTIN_TYPES thành 0 để chỉ xử lý đặc biệt kiểu int, còn các kiểu khác được xử lý qua API mở rộng
    • Với cách này, có thể giảm kích thước nhị phân xuống 31kB
  • Loại bỏ artifact locale

    • Dùng macro FMT_USE_LOCALE để loại bỏ các artifact locale, từ đó giảm kích thước xuống 27kB
  • Đánh đổi giữa tốc độ và kích thước

    • Dùng macro FMT_OPTIMIZE_SIZE để tối ưu cho kích thước, có thể giảm kích thước nhị phân xuống 23kB
  • Loại bỏ phụ thuộc vào thư viện chuẩn C++

    • Vô hiệu hóa exception và dùng tùy chọn -nodefaultlibs để loại bỏ phụ thuộc vào runtime C++
    • Giới thiệu custom allocator dùng mallocfree để giảm kích thước nhị phân xuống 14kB
  • Xác nhận kết quả

    • Dùng lệnh ldd để xác nhận rằng phụ thuộc runtime C++ đã được loại bỏ

Tóm tắt của GN⁺

  • Thư viện {fmt} là một thư viện định dạng cung cấp kích thước nhị phân nhỏ và type safety ở runtime
  • Có thể giảm mạnh kích thước nhị phân thông qua type erasure và các thiết lập macro
  • Việc loại bỏ phụ thuộc vào thư viện chuẩn C++ giúp sử dụng hiệu quả cả trên các hệ thống nhúng
  • Các thư viện cung cấp tính năng tương tự gồm có IOStreams, Boost Format, tinyformat, v.v.

1 bình luận

 
GN⁺ 2024-09-02
Ý kiến trên Hacker News
  • {fmt} về cơ bản là độc lập với locale
  • Việc định dạng số dấu phẩy động cần rất nhiều mã
    • Dự án Dragonbox đáng để đọc vì có mã được tối ưu hóa tốt
  • Bộ cấp phát mặc định của C++ không dùng mallocfree
    • Thắc mắc vì sao bộ cấp phát mặc định của libc++ lại không gọi mallocfree của libc
  • Có một dự án cho phép printf("Hello, World!\n") với kích thước tệp thực thi là 1008 byte
    • Khó so sánh trực tiếp, nhưng cũng đáng tham khảo
  • Trên một hệ thống mà chương trình C với hàm main rỗng có kích thước 6kB, {fmt} thêm chưa đến 10kB vào nhị phân
    • Đây là một phép thử thú vị
  • Đã từng kỳ vọng một thư viện định dạng nhỏ sẽ chỉ cần khoảng 50 byte để in chuỗi và số nguyên
    • Chuỗi chỉ gồm khoảng 4 lệnh
    • Số nguyên chỉ gồm khoảng 20 lệnh
    • Số dấu phẩy động không được dùng trong nhiều chương trình, nên chỉ nên biên dịch khi cần
    • Nếu không gian mã của vi điều khiển chỉ có 2 kilobyte thì sẽ không ai nhét một thư viện định dạng chuỗi 14 kilobyte vào đó
  • Kiểu tối ưu hóa phá vỡ lối suy nghĩ thông thường như vậy rất thú vị
  • Mất một lúc mới nhận ra "14k" nghĩa là "14kB"
  • fmt lúc nào cũng gây ra vấn đề
    • Trên .NET cũng gặp vấn đề tương tự
    • Nếu xử lý nhiều việc định dạng/phân tích số, linker sẽ kéo vào rất nhiều mã liên quan đến số dấu phẩy động và BigInt, khiến kích thước nhị phân tăng lên