
C structs Ausnullen
« | 18 Feb 2024 | »Seit Ewigkeiten nutze ich
struct structtype instance = {0};
um Strukturen am Stack “generisch” mit Nullen zu initialisieren.
Doch … effizient ist das nicht, wenn man auf den generierten Code schaut.
Watcom’s DOS EXEn
Dass Programme “zu groß” sind fällt mir immer nur in der DOS Welt auf. Schließlich hat keiner mehr einen Überblick, ob die Windows CRT oder Linux GLIBC jetzt 100 oder 200 KB einfach nur so zum Spaß anhängen. Man akzeptiert den digitalen Müll einfach.
Aber wenn das Programm zum Auflisten eines Verzeichnisses plötzlich 50 KByte groß wird, und man weiß, dass es funktional nur wenige KB sein dürften, dann wird man wach.
Das tolle ist, dass Watcom beim Kompilieren immer sein .map
Dateien
anlegt, wo jede Funktion und jeder Datenblock mit Adresse und Größe
in einer lesbaren Tabelle landet.
Und dort sind mir dann einige Blöcke aufgefallen, die mehrere Kilobytes
groß waren.
Kopier-Schatten bei der Initialisierung
Wenn ich am Anfang einer C-Funktion die Variablen deklariere, werden die
meistens ausgenullt. Bei struct
s führt ein = { 0 }
dazu, dass alle
Felder (und nicht nur das erste) ausgenullt werden.
Als ich spaßhalber mal eine der Initialisierungen entfernt hatte, sank
sofort die Größe der EXE und auch die Ausdehnung des Datenbereiches in
der .map
Tabelle.
Offenbar erzeugt der Compiler einen statisch mit Nullen ausgefülltes Feld
im Datenbereich und kopiert dieses bei der Initialisierung über die
Variable auf dem Stack.
Das fällt natürlich bei Kleinkram niemandem auf, doch ich erzeuge ja gerne
Puffer-Strukturen, die dann Member mit mehrere Kilobytes haben.
Wenn diese struct
s also bei jeder Initialisierung zu einer versteckten
Leerkopie im Datensegment führen, bläht sich mein Programm unnötig auf.
Lösung: memset
Ich ging also durch meine Initialiserungszeilen durch und ersetzte diese beiden größeren Objekten durch ein:
Resultat: Meine DOS EXEen sind wieder um 20 KByte kleiner geworden.
Der größte Zuwachs kam durch meine Koroutinenen-Implementierung ins
Framework, denn diese wird in DOS jetzt zwingend am Start initialisiert
und dort wurde für mehrere potentielle Stacks Verwaltungsbereich
vorinitialisiert.
Fazit
Tja … es kann gut sein, dass andere Compiler hier besser optimieren und es gar nicht zu solchen Init-Kopien kommt. Aber nachdem man es nicht ausschließen kann, gilt für mich ab sofort die Regel:
struct
s größer als 32 Bytes dürfen nicht mehr per{ 0 }
initialisiert werden.
Und wieder einmal belehrt mich ein Altsystem, dass ich fahrlässig mit Ressourcen umgegangen bin. Und so lernt man aus der Vergangenheit, wie man mit einer Zeile eine bessere Zukunft “aufbauen” kann.