Kompilations-Optimierungs-Leitfaden
1.
Einleitung
Was sind CFLAGS und CXXFLAGS?
CFLAGS und CXXFLAGS sind Umgebungsvariablen, die dazu benutzt werden, um der
GNU Compiler Collection, gcc, mitzuteilen, welche Optionen es während
der Kompilation von Quellcode benutzen soll. CFLAGS sind für Code, der in C
geschrieben ist, während CXXFLAGS für Code in C++ gedacht ist.
Sie können benutzt werden, um die Menge von Debug-Nachrichten für ein Programm
zu verringern, um Level für Fehlerwarnungen zu erhöhen, und, natürlich, um
den produzierten Code zu optimieren. Das GNU
GCC Handbuch beinhaltet eine komplette Liste aller verfügbaren Optionen
und deren Zwecke.
Wie werden sie benutzt?
CFLAGS und CXXFLAGS können auf zwei Wege benutzt werden. Einerseits können sie
pro-Programm mit Makefiles, die von automake generiert werden, benutzt werden.
Jedoch sollte dies nicht getan werden, wenn man Pakete installiert, die sich im
Portage-Baum finden. Setzen Sie Ihre CFLAGS und CXXFLAGS stattdessen in
/etc/make.conf. Auf diese Weise werden alle Pakete mit den
angegebenen Optionen kompiliert.
Befehlsauflistung 1.1: CFLAGS in /etc/make.conf |
CFLAGS="-march=athlon64 -O2 -pipe"
CXXFLAGS="${CFLAGS}"
|
Wie Sie sehen können, wurde CXXFLAGS darauf gesetzt, alle Optionen, die CFLAGS
enthält, zu benutzen. Dies ist meistens die beste Wahl. Sie sollten niemals
zusätzliche Optionen in CXXFLAGS setzen müssen.
Missverständnisse
Auch wenn CFLAGS und CXXFLAGS sehr effektive Wege sind, um aus Quellcode
kleinere und/oder schnellere Binärdateien zu bekommen, können Sie ebenfalls die
Funktionalität Ihres Codes beeinflussen und verschlechtern, dessen Größe
erhöhen, die Ausführungszeit verlangsamen oder sogar zu Kompilationsfehlern
führen!
CFLAGS sind keine Wundermittel; sie machen Ihr System nicht automatisch
schneller oder lassen Ihre Binärdateien weniger Platz auf der Festplatte
belegen. Mehr und mehr Flags hinzuzufügen, im Versuch das System zu optimieren
(oder zu "rice"n), ist ein sicheres Rezept, um Fehler zu erhalten. Es gibt einen
Punkt, den Sie erreichen werden, an dem der Nutzen abnimmt.
Trotz der großen Prahlerei, die Sie im Internet finden werden, schädigen
aggressive CFLAGS und CXXFLAGS Ihre Programme eher, als dass sie ihnen gut tun.
Denken Sie immer daran, dass der Grund, warum die Flags existieren, an erster
Stelle der ist, dass sie nur an bestimmten Stellen aus bestimmten Gründen
benutzt werden sollen. Nur weil ein bestimmtes CFLAG für einen Codeabschnitt
gut ist, heißt das nicht, dass es auch alles andere, was Sie jemals auf Ihrem
Rechner installieren werden, kompiliert!
Bereit?
Da Sie nun über die Gefahren Bescheid wissen, lassen Sie uns einige sichere und
gescheite Optimierungen für Ihren Computer anschauen. Diese halten sie in einer
guten Stellung und werden Sie bei den Entwicklern beliebt machen, wenn Sie das
nächste Mal ein Problem im Bugzilla
melden. (Die Entwickler bitten Sie normalerweise ein Paket mit minimalen CFLAGS
neu zu kompilieren, um zu schauen, ob das Problem bestehen bleibt. Denken Sie
dran: aggressive Flags können den Code ruinieren.)
2.
Optimieren
Die Grundlagen
Das Ziel hinter der Benutzung von CFLAGS und CXXFLAGS ist es, maßgeschneiderten
Code für Ihr System zu erstellen; er sollte sowohl perfekt funktionieren als
auch möglichst klein und schnell sein. Manchmal schließen sich diese Bedingungen
gegenseitig aus, deshalb bleiben wir bei Kombinationen, die als gut
funktionierend bekannt sind. Idealerweise sind sie die besten, die für jede
CPU-Architektur zur Verfügung stehen. Später werden wir die aggressiven Flags
erwähnen, so dass Sie wissen, wonach Sie Ausschau halten müssen. Wir werden
nicht jede einzelne Option, die im gcc Handbuch gelistet ist, besprechen
(es gibt Hunderte), aber die grundlegenden und meist bekannten Flags werden
behandelt.
Notiz:
Wann immer Sie sich nicht sicher sind, was ein Flag eigentlich tut, schauen Sie
sich das entsprechende Kapitel des gcc
Handbuchss an. Wenn Sie weiterhin ratlos sind, versuchen Sie Google oder
die gcc Mailingliste.
|
-march
Die erste und wichtigste Option ist -march. Diese sagt dem Compiler,
welchen Code es für Ihre Prozessor-Architektur (oder
arch) erstellen soll; es bedeutet, dass er Code für eine bestimmte Art
von CPU produzieren soll. Verschiedene CPUs haben verschiedene Fähigkeiten,
unterstützen verschiedene Befehlssätze und haben verschiedene Wege, Code
auszuführen. Das -march Flag weist den Compiler an, speziellen Code für
Ihre CPU zu erstellen - mit allen Fähigkeiten, Merkmalen, Eigenheiten und so
weiter.
Obwohl die Variable CHOST in /etc/make.conf die generell zu
nutzende Architektur spezifiziert, sollte -march trotzdem benutzt werden,
so dass Programme für Ihren spezifischen Prozessor optimiert werden können. x86
und x86-64 CPUs (unter anderem) sollten vom Flag -march Gebrauch machen.
Was für eine CPU haben Sie? Um dies herauszufinden, führen Sie den folgenden
Befehl aus:
Befehlsauflistung 2.1: CPU-Informationen untersuchen |
$ cat /proc/cpuinfo
|
Schauen wir uns -march einmal in Aktion an. Dieses Beispiel ist für einen
älteren Pentium III Chip:
Befehlsauflistung 2.2: /etc/make.conf: Pentium III |
CFLAGS="-march=pentium3"
CXXFLAGS="${CFLAGS}"
|
Hier ist ein weiteres Beispiel für eine 64-Bit-AMD-CPU:
Befehlsauflistung 2.3: /etc/make.conf: AMD64 |
CFLAGS="-march=athlon64"
CXXFLAGS="${CFLAGS}"
|
Wenn Sie sich immer noch nicht sicher sind, was für eine Art von CPU Sie haben,
können Sie auch einfach -march=native verwenden. Wenn dieses Flag
genutzt wird, erkennt GCC Ihren Prozessor und setzt automatisch die
entsprechenden Flags für ihn. Dies sollte jedoch nicht benutzt werden,
wenn Sie vorhaben, Pakete für eine andere CPU zu kompilieren!
Wenn Sie also Pakete auf einem Computer kompilieren, aber vorhaben, sie auf
einem anderen auszuführen (z.B. beim Verwenden eines schnellen Computers zum
Bauen für einen älteren, langsameren), dann benutzen Sie nicht
-march=native. "Native" bedeutet, dass der produzierte Code nur
auf diesem Typ von CPUs laufen wird. Anwendungen, die mit -march=native
auf einer AMD Athlon 64 CPU gebaut wurden, laufen nicht auf einer alten VIA C3
CPU.
Es gibt auch die Flags -mtune und -mcpu. Diese sollten
normalerweise nur benutzt werden, wenn es keine vorhandene -march Option
gibt; bestimmte Prozessor-Architekturen benötigen möglicherweise -mtune
oder sogar -mcpu. Leider verhält sich gcc nicht gerade einheitlich, wenn
man betrachtet, wie sich ein Flag auf verschiedenen Architekturen verhält.
Auf x86 und x86-64 CPUs erstellt -march speziellen Code für die CPU unter
Benutzung aller Befehlssätze und der korrekten ABI; der Code wird keine
Abwärtskompatibilität für ältere/andere CPUs enthalten. Wenn Sie den Code nicht
auf anderen Systemen als dem, auf dem Sie Gentoo benutzen, ausführen müssen,
verwenden Sie weiterhin -march. Sie sollten die Benutzung von
-mtune nur in Erwägung ziehen, wenn Sie Code für ältere CPUs wie i386 und
i486 generieren müssen. -mtune produziert generischeren Code als
-march; obwohl es den Code für eine bestimmte CPU anpassen wird, achtet
es nicht auf vorhandene Befehlssätze und die ABI. Benutzen Sie -mcpu
nicht auf x86- oder x86-64-Systemen, da es auf diesen Architekturen als
"abgelehnt" betrachtet wird.
Nur nicht-x86/x86-64 CPUs (wie Sparc, Alpha und PowerPC) benötigen -mtune
oder -mcpu anstelle von -march. Auf diesen Architekturen verhält
sich -mtune/-mcpu manchmal wie -march (auf x86/x86-64) . .
. aber mit einem anderen Flag-Namen. Wie bereits gesagt, gccs Verhalten
und Flag-Benennung ist einfach nicht auf allen Architekturen einheitlich.
Schauen Sie sich also auf jeden Fall das gcc Handbuch
an, um zu bestimmen, welches Flag Sie auf Ihrem System verwenden sollten.
Notiz:
Für weitere vorgeschlagene -march/-mtune/-mcpu
Einstellungen, lesen Sie bitte Kapitel 5 des entsprechenden Gentoo Installationshandbuchs für Ihre
Architektur. Lesen Sie ebenfalls sowohl die Liste der architekturspezifischen
Optionen des gcc Handbuchs, als auch detailliertere Erklärungen zu
den Unterschieden zwischen -march, -mcpu und -mtune.
|
-O
Das Nächste ist die Variable -O. Diese kontrolliert das allumfassende
Level der Optimierung. Durch diese Variable erhöht sich die Zeit zum Kompilieren
und es wird eventuell mehr Speicher benötigt, insbesondere wenn Sie das
Optimierungslevel erhöhen.
Es gibt fünf -O Einstellungen: -O0, -O1, -O2,
-O3 und -Os. Sie sollten nur eine davon in
/etc/make.conf benutzen.
Bis auf -O0 aktivieren alle -O Einstellungen einige zusätzliche
Flags; lesen Sie also das Kapitel über die Optimierungs-Optionen,
um zu erfahren, welche Flags auf welchem -O Level aktiviert werden, und
um Erklärungen zu diesen zu erhalten.
Lassen Sie uns jeden Optimierungslevel untersuchen:
-
-O0: Dieser Level (es ist der Buchstabe "O" gefolgt von einer Null)
schaltet die Optimierung komplett aus und ist Standard, wenn kein -O
Level in CFLAGS oder CXXFLAGS angegeben wird. Ihr Code wird nicht optimiert;
dies ist normalerweise nicht gewünscht.
-
-O1: Dies ist der grundlegendste Optimierungslevel. Der Compiler wird
versuchen, schnelleren und kleineren Code zu produzieren, ohne zu viel
Kompilationszeit zu benötigen. Es ist ziemlich simpel, aber sollte in jedem
Fall funktionieren.
-
-O2: Ein Schritt weiter als -O1. Dies ist der
empfohlene Level der Optimierung, sofern Sie keine besonderen
Anforderungen haben. -O2 wird einige weitere Flags zusätzlich zu
denen, die von -O1 aktiviert werden, einschalten. Mit -O2 wird
der Compiler versuchen, die Performanz des Codes zu erhöhen, ohne auf die
Größe zu achten und ohne zu viel Kompilationszeit zu benötigen.
-
-O3: Dies ist nicht nur der höchstmögliche Optimierungslevel, sondern
auch der riskanteste. Es wird mehr Zeit benötigt werden, um den Code mit
dieser Option zu kompilieren, und sollte nicht systemweit mit gcc
4.x benutzt werden. Das Verhalten von gcc hat sich seit Version
3.x maßgeblich geändert. In 3.x waren die Ausführungszeiten mit -O3
geringfügig schneller gegenüber -O2, aber dies ist mit gcc
4.x nicht länger der Fall. Das Kompilieren aller Pakete mit -O3
wird in größeren Binärdateien, die mehr Speicher benötigen,
resultieren, und wird die Wahrscheinlichkeit auf Kompilationsfehler und
unerwartetes Programmverhalten (inklusive Fehler) erhöhen. Die Nachteile
überwiegen die Vorteil; das Prinzip des abnehmenden Nutzens zeigt sich.
Das Benutzen von -O3 ist für gcc 4.x nicht empfohlen.
-
-Os: Dieser Level wird Ihren Code zur Größe hin optimieren. Er
aktiviert alle -O2 Optionen, die nicht die Größe des generierten
Codes vergrößern. Er kann für Maschinen mit extrem limitiertem
Festplattenplatz und/oder mit CPUs mit kleinen Cache-Größen nützlich sein.
Jedoch kann dieser Level einige Probleme bereiten, weshalb er von vielen
Ebuilds im Portage-Baum herausgefiltert wird. Die Benutzung von -Os
ist nicht empfohlen.
Wie bereits erwähnt, ist -O2 das empfohlene Optimierungslevel. Wenn
Paketkompilationen fehlschlagen, stellen Sie sicher, dass Sie -O3 nicht
verwenden. Zum Testen und zur Sicherheit können Sie versuchen, Ihre CFLAGS und
CXXFLAGS auf ein niedrigeres Optimierungslevel zu setzen, z.B. -O1 oder
sogar -O0 -g2 -ggdb (für Fehlerberichte und zum Prüfen auf mögliche
Probleme), und das Paket neu zu kompilieren.
-pipe
Ein bekanntes Flag ist -pipe. Dieses Flag hat eigentlich keinen Effekt
auf den generierten Code, aber es macht den Kompilationsprozess schneller. Es
weist den Compiler an, Pipes anstatt temporärer Dateien während der
verschiedenen Stufen der Kompilation zu verwenden, was mehr Speicher
beansprucht. Auf Systemen mit wenig Speicher könnte gcc gekillt wird. In diesem
Fall sollten Sie dieses Flag nicht verwenden.
-fomit-frame-pointer
Dies ist ein gebräuchliches Flag, um die Größe des generierten Codes zu
reduzieren. Es wird auf allen -O Leveln (außer -O0) auf
Architekturen, auf denen es das Debuggen nicht stört (z.B. x86-64), aktiviert,
aber möglicherweise müssen Sie es selbst aktivieren, indem Sie es zu Ihren
Flags hinzufügen. Obwohl das GNU gcc Handbuch nicht alle Architekturen,
auf denen es durch -O aktiviert wird, spezifiziert, werden Sie es
explizit für x86 aktivieren müssen. Jedoch wird das Debuggen durch die
Benutzung dieses Flags hart bis unmöglich gemacht.
Um genau zu sein, macht es die Fehlersuche in Anwendungen, die in Java
geschrieben sind, um einiges schwieriger, auch wenn Java nicht der einzige Code
ist, der von diesem Flag betroffen ist. Während das Flag also helfen kann, macht
es das Debuggen schwieriger; bestimmte Backtraces werden nutzlos sein. Wenn Sie
jedoch nicht planen, viel Software-Debugging durchzuführen und kein weiteres
debugging-verwandtes CFLAGS wie -ggdb hinzugefügt haben, dann können Sie
versuchen -fomit-frame-pointer zu benutzen.
Wichtig:
Kombinieren Sie -fomit-frame-pointer nicht mit dem ähnlichen Flag
-momit-leaf-frame-pointer. Die Benutzung des letzteren Flags ist
nicht empfohlen, da -fomit-frame-pointer den Job bereits richtig
erledigt. Weiterhin wurde bewiesen, dass -momit-leaf-frame-pointer die
Performanz des Codes negativ beeinflusst.
|
-msse, -msse2, -msse3, -mmmx, -md3now
Diese Flags aktivieren die Befehlssätze SSE, SSE2, SSE3, MMX und 3DNow! für die Architekturen x86
und x86-64. Diese sind in erster Linie für Multimedia-, Spiele- und intensive
Gleitpunktzahl-Berechnungen und -Anwendungen nützlich, auch wenn sie einige
weitere mathematische Verbesserungen enthalten. Diese Befehlssätze finden sich
in moderneren CPUs.
Wichtig:
Stellen Sie sicher, dass Ihre CPU diese unterstützt, indem Sie cat
/proc/cpuinfo ausführen. Die Ausgabe wird alle zusätzlich unterstützten
Befehlssätze enthalten. Beachten Sie, dass pni einfach ein anderer Name
für SSE3 ist.
|
Normalerweise müssen Sie keines dieser Flags zu /etc/make.conf
hinzufügen, so lange Sie die korrekte -march benutzen (z.B.
-march=nocona impliziert -msse3). Einige erwähnenswerte Ausnahmen
sind neuere VIA und AMD64 CPUs, welche Befehle unterstützen, die nicht von
-march impliziert werden (z.B. SSE3). Für solche CPUs müssen Sie diese
zusätzlichen Flags, falls nötig, hinzufügen, nachdem Sie die Ausgabe von
cat /proc/cpuinfo kontrolliert haben.
Notiz:
Sie sollten die Liste
der x86 und x86-64 spezifischen Flags anschauen, um zu erfahren, welche
Befehlssätze durch das richtige CPU-Typ-Flag aktiviert werden. Wenn ein Befehl
aufgelistet ist, müssen Sie ihn nicht angeben; er wird durch die richtige
-march Einstellung eingeschaltet.
|
3.
Optimierungs-FAQ
Aber ich bekomme bessere Performanz mit -funroll-loops -fomg-optimize!
Nein, Sie glauben nur, dass Sie bessere Performanz bekommen, weil Sie
jemand überzeugt hat, dass mehrere Flags besser sind. Aggressive Flags werden
Ihren Anwendungen nicht gut tun, wenn sie systemweit genutzt werden. Sogar das
gcc Handbuch
sagt, dass die Benutzung von -funroll-loops und -funroll-all-loops
den Code größer und langsamer macht. Trotzdem sind diese beiden Flags, neben
-ffast-math, -fforce-mem, -fforce-addr und ähnlichen Flags,
weiterhin sehr beliebt unter den Ricern, welche die größten Rechte zum Prahlen
haben wollen.
Die Wahrheit ist, dass dies sehr gefährliche und aggressive Flags sind. Schauen
Sie sich in den Gentoo Foren und
im Bugzilla um, um zu sehen, was diese
Flags tun: nichts Gutes!
Sie brauchen diese Flags nicht global in CFLAGS oder CXXFLAGS zu verwenden. Sie
werden Ihre Performanz einschränken. Sie hören sich vielleicht danach an, als
hätten sie ein hochperformantes, innovatives und hochaktuelles System, aber sie
tun nichts, außer Ihren Code zuzumüllen und Ihre Bugs als INVALID oder WONTFIX
schließen zu lassen.
Sie brauchen solche gefährlichen Flags nicht. Benutzen Sie sie nicht.
Bleiben Sie bei den Grundlagen: -march, -O und -pipe.
Wie sieht es mit -O Leveln höher als 3 aus?
Einige Benutzer prahlen über noch bessere Performanz, die sie durch Benutzung
von -O4, -O9 und so weiter erhalten, aber die Wahrheit ist, dass
-O Level größer als 3 keinen Effekt haben. Der Compiler akzeptiert zwar
CFLAGS wie -O4, aber eigentlich tut er nichts mit ihnen. Er führt nur
die Optimierungen für -O3 aus, mehr nicht.
Sie brauchen mehr Beweise? Untersuchen Sie den gcc Quelltext:
Befehlsauflistung 3.1: -O Quelltext |
if (optimize >= 3)
{
flag_inline_functions = 1;
flag_unswitch_loops = 1;
flag_gcse_after_reload = 1;
/* Allow even more virtual operators. */
set_param_value ("max-aliased-vops", 1000);
set_param_value ("avg-aliased-vops", 3);
}
|
Wie Sie sehen können, wird jeder Wert größer als 3 einfach so behandelt wie
-O3.
Wie sieht es mit überflüssigen Flags aus?
Oft werden CFLAGS und CXXFLAGS, welche durch verschiedene -O Level
eingeschaltet werden, erneut in /etc/make.conf gesetzt. Manchmal
geschieht dies durch Ignoranz, aber es wird auch getan, um das Filtern oder
Ersetzen von Flags zu vermeiden.
Filtern oder Ersetzen von Flags wird in vielen der Ebuilds im Portage-Baum
durchgeführt. Dies geschieht gewöhnlich, da die Pakete mit bestimmten -O
Leveln nicht kompilieren oder wenn der Quelltext zu sensibel für zusätzliche
Flags ist. Das Ebuild filtert entweder alle CFLAGS und CXXFLAGS heraus oder
ersetzt -O mit einem anderem Level.
Das Gentoo
Entwickler Handbuch beschreibt, wo und wie das Filtern/Ersetzen von Flags
funktioniert.
Es ist möglich das Filtern von -O zu umgehen, indem Sie jedes Flag für
ein bestimmtes Level, z.B. -O3, erneut auflisten:
Befehlsauflistung 3.2: Redundante CFLAGS angeben |
CFLAGS="-O3 -finline-functions -funswitch-loops"
|
Dies ist jedoch keine kluge Sachen, zu tun. Das Filter von CFLAGS
hat einen Grund! Wenn Flags gefiltert werden, bedeutet dies, dass es unsicher
ist, ein Paket mit diesen Flags zu bauen. Es ist nicht sicher ein
komplettes Syste mit -O3 zu kompilieren, falls einige Flags, die auf
diesem Level aktiviert werden, Probleme für bestimmte Pakete bereiten. Aus
diesem Grunde sollten Sie die Entwickler, die diese Pakete verwalten, nicht
"austricksen". Vertrauen Sie den Entwicklern. Das Filtern und Ersetzen
von Flags geschieht zu Ihrem Gunsten! Wenn ein Ebuild alternative Flags
angibt, versuchen Sie nicht, dies zu umgehen.
Sie werden höchstwahrscheinlich weiterhin Probleme bekommen, wenn sie Pakete
mit inakzeptablen Flags bauen. Wenn Sie Ihre Probleme im Bugzilla berichten,
werden die Flags, die Sie in /etc/make.conf benutzen, lesbar sein
und Ihnen wird gesagt, dass Sie versuchen sollen, ohne diese Flags erneut zu
kompilieren. Sparen Sie sich diese Probleme, indem Sie redundante Flags nicht
benutzen! Nehmen Sie nicht automatisch an, dass Sie es besser wissen, als die
Entwickler.
Was ist mit LDFLAGS?
Die Gentoo-Entwickler haben in den base-Profilen bereits grundlegende, sichere
LDFLAGS gesetzt, daher müssen Sie diese nicht ändern.
Kann ich pro-Paket Flags benutzen?
Es gibt keine unterstützte Methode, um CFLAGS oder andere Variablen auf einer
pro-Paket-Basis zu verwenden, auch wenn es einige ziemlich
missbrauchende Wege gibt, Portage zu diesem Verhalten zu zwingen.
Warnung:
Sie sollten nicht versuchen, Portage zu zwingen, pro-Paket-Flags zu
verwenden, da dies in keinster Weise unterstützt wird und Bug-Reports erheblich
erschwert. Setzen Sie Ihre Flags einfach in /etc/make.conf, so dass
sie systemweit genutzt werden.
|
4.
Ressourcen
Die folgenden Ressourcen können Ihnen helfen, wenn Sie die Optimierung genauer
verstehen wollen:
-
Das GNU gcc
Handbuch
-
Kapitel 5 des Gentoo
Installationshandbuchs
- man make.conf
- Wikipedia
-
Acovea, ein
Benchmarking- und Testpaket, welches Ihnen helfen kann zu bestimmen, wie
unterschiedliche Compilerflags interagieren und den generierten Code
betreffen, auch wenn die Vorschläge nicht für systemweite Benutzung
angemessen sind. Es ist in Portage verfügbar: emerge acovea.
- Die Gentoo Foren
Die Inhalte dieses Dokuments sind, sofern nicht explizit
anders genannt, unter der Creative Commons -
Namensnennung / Weitergabe Lizenz lizenziert. Die Gentoo Name and Logo
Usage Guidelines treffen zu.
|
|
Seite aktualisiert 26. Juli 2010 |
Die Originalversion dieses Dokuments wurde zuletzt am 25. Dezember 2012 aktualisiert |
Zusammenfassung:
Dieser Leitfaden stellt eine Einführung zur Optimierung von kompiliertem Code
mit Benutzung von sicheren und gescheiten CFLAGS und CXXFLAGs dar. Er beschreibt
ebenfalls die Theorie hinter der Optimierung im Allgemeinen.
|
Joshua Saddler
Author
Tobias Heinlein
Übersetzer
|
|
Donate to support our development efforts.
|
|
|