Gentoo Logo

Kompilations-Optimierungs-Leitfaden

Inhalt:

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


Drucken

Seite aktualisiert 26. Juli 2010

Die Originalversion dieser Übersetzung wird nicht länger gepflegt

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.

Copyright 2001-2014 Gentoo Foundation, Inc. Questions, Comments? Contact us.