1 ポイント 投稿者 GN⁺ 2024-09-02 | 1件のコメント | WhatsAppで共有

{fmt} ライブラリのバイナリサイズ最適化

  • {fmt} ライブラリの紹介

    • {fmt} は小さなバイナリサイズで知られるフォーマットライブラリ
    • IOStreams、Boost Format、tinyformat などと比べて、関数呼び出しあたりのコードサイズがはるかに小さい
    • 型消去(type erasure)によってテンプレートの負荷を最小化する
  • 型消去によるフォーマット

    • format 関数は処理を vformat 関数に委譲する
    • 出力イテレータやその他の出力型も、特別に設計されたバッファAPIを通じて型消去される
    • テンプレートの使用を最小限にして、バイナリサイズとビルド時間を削減する
  • サンプルコード

    #include <fmt/base.h>
    int main() { fmt::print("The answer is {}.", 42); }
    
    • 上のコードは、IOStreams のコードよりもはるかに小さいサイズでコンパイルされる
    • printf と比べてもサイズは同程度で、実行時の型安全性を提供する
  • バイナリサイズの最適化

    • 2020年にライブラリサイズを100kB以下に抑える作業を実施した
    • 最新版(11.0.2)のバイナリサイズは75kB
    • ロケールサポートを無効化すると、サイズを71kBまで縮小できる
  • Bloaty ツールを使った分析

    • 数値フォーマット、とくに浮動小数点数のフォーマットが大きな割合を占める
    • 浮動小数点サポートが不要であれば、これを無効化できる
  • 型別フォーマットの最適化

    • FMT_BUILTIN_TYPES マクロを 0 に設定し、int 型だけを特別扱いし、残りの型は拡張API経由で処理する
    • この方法でバイナリサイズを31kBまで削減できる
  • ロケールアーティファクトの除去

    • FMT_USE_LOCALE マクロを使ってロケールアーティファクトを除去すると、サイズを27kBまで縮小できる
  • 速度とサイズのトレードオフ

    • FMT_OPTIMIZE_SIZE マクロを使ってサイズ最適化を行うと、バイナリサイズを23kBまで削減できる
  • C++標準ライブラリ依存の除去

    • 例外を無効化し、-nodefaultlibs オプションを使って C++ ランタイム依存を除去する
    • mallocfree を使うカスタムアロケータを導入することで、バイナリサイズを14kBまで削減できる
  • 結果の確認

    • ldd コマンドを使って、C++ ランタイム依存が除去されたことを確認する

GN⁺のまとめ

  • {fmt} ライブラリは、小さなバイナリサイズと実行時の型安全性を提供するフォーマットライブラリ
  • 型消去とマクロ設定によって、バイナリサイズを大幅に削減できる
  • C++標準ライブラリ依存を除去することで、組み込みシステムでも効率的に利用できる
  • 類似の機能を提供するライブラリには、IOStreams、Boost Format、tinyformat などがある

1件のコメント

 
GN⁺ 2024-09-02
Hacker Newsのコメント
  • {fmt} は基本的にロケール非依存
  • 浮動小数点のフォーマットには多くのコードが必要
    • Dragonbox プロジェクトは最適化されたコードとして一読の価値がある
  • C++ のデフォルトアロケータは malloc と free を使わない
    • libc++ のデフォルトアロケータが libc の malloc と free を呼ばない理由が気になる
  • printf("Hello, World!\n") を実行ファイルサイズ 1008 バイトで実現するプロジェクトがある
    • 直接比較は難しいが参考にはなる
  • 空の main 関数を持つ C プログラムが 6kB のシステムでは、{fmt} はバイナリに 10kB 未満を追加する
    • 興味深いテストだ
  • 小さなフォーマットライブラリなら、文字列と整数の出力に約 50 バイトしか必要としないだろうと期待していた
    • 文字列は約 4 命令で構成される
    • 整数は約 20 命令で構成される
    • 浮動小数点は多くのプログラムで使われないため、必要なときだけコンパイルすべき
    • マイクロコントローラのコード空間が 2 キロバイトしかないなら、14 キロバイトの文字列フォーマットライブラリは含めない
  • こうした発想の枠を超えた最適化はとても楽しい
  • "14k" が "14kB" を意味していると気づくまで少し時間がかかった
  • fmt はいつも問題を引き起こす
    • .NET でも同じ問題が起きる
    • 数値のフォーマットやパースを多く扱うと、リンカが大量の浮動小数点および BigInt 関連コードを取り込み、バイナリサイズが大きくなる