1 Punkte von GN⁺ 2024-09-02 | 1 Kommentare | Auf WhatsApp teilen

Optimierung der Binärgröße der {fmt}-Bibliothek

  • Einführung in die {fmt}-Bibliothek

    • {fmt} ist eine Formatierungsbibliothek, die für ihre kleine Binärgröße bekannt ist
    • Im Vergleich zu IOStreams, Boost Format, tinyformat usw. ist die Codegröße pro Funktionsaufruf deutlich kleiner
    • Durch Type Erasure wird der Template-Overhead minimiert
  • Formatierung durch Type Erasure

    • Die Funktion format delegiert die Arbeit an die Funktion vformat
    • Ausgabe-Iteratoren und andere Ausgabetypen werden ebenfalls über eine speziell entworfene Buffer-API per Type Erasure abstrahiert
    • Durch die Minimierung des Template-Einsatzes werden Binärgröße und Build-Zeit reduziert
  • Beispielcode

    #include <fmt/base.h>
    int main() { fmt::print("The answer is {}.", 42); }
    
    • Der obige Code wird zu einer deutlich kleineren Größe kompiliert als entsprechender IOStreams-Code
    • Im Vergleich zu printf ist die Größe ähnlich, zugleich wird Runtime-Typsicherheit geboten
  • Optimierung der Binärgröße

    • 2020 wurde daran gearbeitet, die Bibliotheksgröße auf unter 100 kB zu reduzieren
    • Die Binärgröße der aktuellen Version (11.0.2) beträgt 75 kB
    • Wird die Locale-Unterstützung deaktiviert, lässt sich die Größe auf 71 kB reduzieren
  • Analyse mit dem Tool Bloaty

    • Zahlenformatierung, insbesondere die Formatierung von Gleitkommazahlen, macht einen großen Anteil aus
    • Wenn keine Unterstützung für Gleitkommazahlen benötigt wird, kann diese deaktiviert werden
  • Optimierung der Formatierung nach Typen

    • Durch Setzen des Makros FMT_BUILTIN_TYPES auf 0 wird nur der Typ int speziell behandelt, während die übrigen Typen über die Erweiterungs-API verarbeitet werden
    • Mit dieser Methode kann die Binärgröße auf 31 kB reduziert werden
  • Entfernung von Locale-Artefakten

    • Wenn mit dem Makro FMT_USE_LOCALE Locale-Artefakte entfernt werden, lässt sich die Größe auf 27 kB reduzieren
  • Trade-off zwischen Geschwindigkeit und Größe

    • Wird die Größe mit dem Makro FMT_OPTIMIZE_SIZE optimiert, kann die Binärgröße auf 23 kB reduziert werden
  • Entfernung der Abhängigkeit von der C++-Standardbibliothek

    • Durch das Deaktivieren von Ausnahmen und die Verwendung der Option -nodefaultlibs wird die Abhängigkeit von der C++-Runtime entfernt
    • Durch die Einführung eines benutzerdefinierten Allocators mit malloc und free kann die Binärgröße auf 14 kB reduziert werden
  • Überprüfung des Ergebnisses

    • Mit dem Befehl ldd wird bestätigt, dass die Abhängigkeit von der C++-Runtime entfernt wurde

Zusammenfassung von GN⁺

  • Die {fmt}-Bibliothek ist eine Formatierungsbibliothek, die eine kleine Binärgröße und Runtime-Typsicherheit bietet
  • Durch Type Erasure und Makro-Konfigurationen lässt sich die Binärgröße erheblich reduzieren
  • Durch das Entfernen der Abhängigkeit von der C++-Standardbibliothek lässt sie sich auch in Embedded-Systemen effizient einsetzen
  • Bibliotheken mit ähnlicher Funktionalität sind unter anderem IOStreams, Boost Format und tinyformat

1 Kommentare

 
GN⁺ 2024-09-02
Hacker-News-Kommentare
  • {fmt} ist standardmäßig nicht von der Locale abhängig
  • Für die Formatierung von Fließkommazahlen wird viel Code benötigt
    • Das Dragonbox-Projekt ist mit seinem optimierten Code lesenswert
  • Der Standard-Allocator von C++ verwendet nicht malloc und free
    • Es wird hinterfragt, warum der Standard-Allocator von libc++ nicht malloc und free aus libc aufruft
  • Es gibt ein Projekt, das printf(Hello, World!\n") mit einer ausführbaren Dateigröße von 1008 Byte ermöglicht
    • Ein direkter Vergleich ist schwierig, aber als Referenz interessant
  • Auf einem System, auf dem ein C-Programm mit leerer main-Funktion 6 kB groß ist, fügt {fmt} dem Binärprogramm weniger als 10 kB hinzu
    • Das ist ein interessanter Test
  • Man hätte erwartet, dass eine kleine Formatierungsbibliothek zum Ausgeben von Strings und Ganzzahlen etwa 50 Byte benötigt
    • Strings bestehen aus ungefähr 4 Instruktionen
    • Ganzzahlen bestehen aus ungefähr 20 Instruktionen
    • Fließkommazahlen werden in vielen Programmen nicht verwendet und sollten daher nur bei Bedarf kompiliert werden
    • Wenn der Code-Speicher eines Mikrocontrollers 2 Kilobyte beträgt, nimmt man keine 14-Kilobyte-String-Formatierungsbibliothek auf
  • Solche Optimierungen außerhalb des gewohnten Denkschemas machen sehr viel Spaß
  • Es hat eine Weile gedauert, zu begreifen, dass „14k“ „14 kB“ bedeutet
  • fmt verursacht immer Probleme
    • Dasselbe Problem tritt auch in .NET auf
    • Wenn man viel mit Zahlenformatierung/-Parsing arbeitet, zieht der Linker viel Fließkomma- und BigInt-bezogenen Code hinein, wodurch die Binärgröße wächst