GCC: Statische Optimierungen

Statische Bibliotheken gehören bekanntlich zu meinen Lieblingen, und auf Plattformen wie DOS sind sie sowieso eine Notwendigkeit.

Doch trotz Link-Time-Optimization und anderem Schnickschnack sind die fertigen Programme meist größer als unter Windows. Und teilweise sogar verdammt übergroß.


Ein moderner MSVC erzeugt gerne eine 100 KB große EXE für ein einfaches hello_world Programm, wenn die C-Runtime statisch gelinkt wird. Der GCC unter Linux oder OpenWatcom für DOS sind da schon kompakter mit 10 bis 40 KB.
Das ist auch halbwegs “OK”, wenn man an die vielen Patches und System-Call Wrapper denkt.

Trotzdem fällt mir auf, dass MSVC Binaries gerade beim GATE Projekt nicht so rasant anwachsen, wie auf anderen Plattformen.
Sowohl der GCC wie auch OpenWatcom produzieren gleich 200 KB große Binaries, wenn sie gegen die statische GATE-Core Bibliothek linken.

Ein Blick in die Binaries erklärt das auch:

Da waren alle Funktionen der GATE Bibliotheken enthalten.

Während der MSVC nur die benutzten Funktionen in die EXE packt, legen GCC und Watcom auch alle unbenutzten Funktionen bei.

Es wird also Zeit für ein Build-Update.

GCC

Jede nicht statische (also extern) Funktion wird im GCC als “öffentlich” markiert. Das bedeutet in .so Shared Objects, dass sie exportiert wird.
Programme exportieren jedoch nichts, sondern rufen nur jene Ketten von Funktionen auf, die irgendwo in main() anfangen.
Doch da der GCC auch alles unbenutzte beilegt, werden Programme unnötig groß.

Lösung: attribute ((visibility (“hidden”)))

Ich baue Shared-Libs grundsätzlich mit der default-Visibility hidden, doch bei Programmen habe ich das bisher nie benutzt.
Die Alternative ist natürlich, dass man das “Exportier-Makro” aller Funktionen explizit auf __attribute__ ((visibility ("hidden"))) schaltet.

1#if defined(GATE_COMPILER_GCC)
2#  define GATE_API_EXPORT __attribute__((visibility ("default")))
3#  define GATE_API_IMPORT
4#  define GATE_API_LOCAL __attribute__ ((visibility ("hidden")))
5#endif
 1#if defined(GATE_SHARED_LIBS)
 2#  if defined(GATE_CORE_EXPORTS)
 3#    define GATE_CORE_API GATE_API_EXPORT
 4#  else
 5#    define GATE_CORE_API GATE_API_IMPORT
 6#  endif
 7#else
 8#  define GATE_CORE_API GATE_API_LOCAL
 9#endif
10
11GATE_CORE_API int gate_foo(int something);

Watcom

Unter DOS war die Sache noch viel auffälliger. Ich musste das Speicher-Modell auf Medium erhöhen, damit alle Daten in einer GATE-Hello-World EXE Platz hatten. Auch hier zeigte die erstellte .map Datei, dass alle Funktionen der Bibliothek samt Daten und VTBL Strukturen in die EXE gewandert waren.

Lösung: -zm und opt el

Wenn man das Compiler-Flag -zm anhängt, werden Funktionen in eigene Segmente ausgelagert. Und die Linker-Option ELIMINATE, löscht dann alle diese Fragmente, die nicht vom Programm aus aufgerufen werden.

In CMake für Watcom sieht dass dann so aus:

1set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -zm")
2set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -zm")
3set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} opt el")

Fazit

Kleine EXE, große Wirkung!

Gerade, wenn man OpenSSL oder libSSH2 statisch in sein Programm mit aufnimmt, kommen hunderte wenn nicht tausende Funktionen hinzu, die man nie braucht. Alle bekommt man leider nie weg, weil sie oft in Strukturen zusammengefasst werden, aber wenn man zumindest einige “eliminieren” kann, schrumpft das finale Programm dann schon ordentlich.

So wurde meine DOS-EXE von 135 KB auf 35 KB reduziert.
Und der GCC verbuchte für das alte 200 KB Binary nach der Kur nur noch 25 KB.

Damit wurde der MSVC mit seinen 100 KB Babyspeck deutlich geschlagen und die großen 4-MB Programme sind jetzt auf Windows und Linux ziemlich gleich groß.

Wenn also etwas unnatürlich groß aus der Compiler-Fertigung herauskommt, gilt:

Nicht verzagen, Compiler-Handbuch fragen!

Denn dort findet man (nach langem Suchen) meist den Grund und die Lösung.

📧 📋 🐘 | 🔔
 

Meine Dokus über:
 
Weitere externe Links zu:
Alle extern verlinkten Webseiten stehen nicht in Zusammenhang mit opengate.at.
Für deren Inhalt wird keine Haftung übernommen.



Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!