Wie viele GCC-Optimierungsstufen gibt es?

101

Wie viele GCC- Optimierungsstufen gibt es?

Ich habe gcc -O1, gcc -O2, gcc -O3 und gcc -O4 ausprobiert

Wenn ich eine wirklich große Anzahl verwende, funktioniert es nicht.

Ich habe es jedoch versucht

gcc -O100

und es kompiliert.

Wie viele Optimierungsstufen gibt es?

Neuromant
quelle
13
@minitech Welches FM schaust du dir an? Selbst mit man gccauf Cygwin (12000 ungerade Zeilen) können Sie -Oalles suchen und finden, was die Antworten unten angeben, und noch einige mehr.
Jens
1
@minmaxavg Nach dem Lesen der Quelle stimme ich Ihnen nicht zu: Alles, was größer ist als 3das gleiche wie 3(solange es nicht intüberläuft). Siehe meine Antwort .
Ciro Santilli 法轮功 冠状 病 六四 事件 18
1
Tatsächlich verfügt GCC über viele andere Flags zur Feinabstimmung von Optimierungen. -fomit-stack-pointer ändert den generierten Code.
Basile Starynkevitch

Antworten:

141

Um pedantisch zu sein, gibt es 8 verschiedene gültige -O-Optionen, die Sie gcc geben können, obwohl es einige gibt, die dasselbe bedeuten.

In der Originalversion dieser Antwort wurden 7 Optionen angegeben. GCC hat seitdem hinzugefügt -Og, um die Summe auf 8 zu bringen

Von der Manpage:

  • -O (Gleich wie -O1)
  • -O0 (Keine Optimierung durchführen, Standardeinstellung, wenn keine Optimierungsstufe angegeben ist)
  • -O1 (minimal optimieren)
  • -O2 (mehr optimieren)
  • -O3 (noch mehr optimieren)
  • -Ofast (sehr aggressiv optimieren, bis die Standardkonformität verletzt wird)
  • -Og (Optimieren der Debugging-Erfahrung. -Og ermöglicht Optimierungen, die das Debuggen nicht beeinträchtigen. Dies sollte die Optimierungsstufe der Wahl für den Standard-Zyklus zum Bearbeiten, Kompilieren und Debuggen sein, die ein angemessenes Optimierungsniveau bietet und gleichzeitig eine schnelle Kompilierung und eine gute Debugging-Erfahrung gewährleistet. )
  • -Os(. Optimize for Größe -Osermöglicht es, alle -O2Optimierungen , die typischerweise keine Codegröße erhöhen Sie führt auch weitere Optimierungen entwickelt , die Codegröße zu reduzieren.. -OsDeaktiviert die folgenden Optimierungen: -falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -freorder-blocks-and-partition -fprefetch-loop-arrays -ftree-vect-loop-version)

Es kann auch plattformspezifische Optimierungen geben, wie OSpa feststellt, hat @pauldoo -Oz

Tal
quelle
23
Wenn Sie unter Mac OS X entwickeln, gibt es eine zusätzliche -OzEinstellung, die "aggressiver für die Größe optimieren als -Os" ist: developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/…
pauldoo
6
Hinweis: O3 ist nicht unbedingt besser als O2, auch wenn der Name dies nahelegt. Versuchen Sie beides.
Johan d
1
@pauldoo 404 Seite, ersetzen durch archive.org
noɥʇʎԀʎzɐɹƆ
Es gibt auch -Ogalle Optimierungsoptionen, die das Debuggen nicht beeinträchtigen
einpoklum
47

Lassen Sie uns den Quellcode von GCC 5.1 interpretieren, um zu sehen, was passiert, -O100da dies auf der Manpage nicht klar ist.

Wir werden daraus schließen, dass:

  • Alles, was darüber liegt -O3, INT_MAXist dasselbe wie -O3, aber das könnte sich in Zukunft leicht ändern. Verlassen Sie sich also nicht darauf.
  • GCC 5.1 führt ein undefiniertes Verhalten aus, wenn Sie Ganzzahlen eingeben, die größer als sind INT_MAX.
  • Das Argument kann nur Ziffern haben oder schlägt ordnungsgemäß fehl. Dies schließt insbesondere negative ganze Zahlen wie aus-O-1

Konzentrieren Sie sich auf Unterprogramme

Zunächst erinnert , dass GCC ist nur ein Front-End für cpp, as, cc1, collect2. Ein kurzer ./XXX --helpsagt das nur collect2und cc1nimm -O, also konzentrieren wir uns auf sie.

Und:

gcc -v -O100 main.c |& grep 100

gibt:

COLLECT_GCC_OPTIONS='-O100' '-v' '-mtune=generic' '-march=x86-64'
/usr/local/libexec/gcc/x86_64-unknown-linux-gnu/5.1.0/cc1 [[noise]] hello_world.c -O100 -o /tmp/ccetECB5.

so -Owurde an beide cc1und weitergeleitet collect2.

O gemeinsam.opt

common.opt ist ein GCC-spezifisches CLI-Optionsbeschreibungsformat, das in der internen Dokumentation beschrieben und von opth-gen.awk und optc-gen.awk in C übersetzt wird .

Es enthält die folgenden interessanten Zeilen:

O
Common JoinedOrMissing Optimization
-O<number>  Set optimization level to <number>

Os
Common Optimization
Optimize for space rather than speed

Ofast
Common Optimization
Optimize for speed disregarding exact standards compliance

Og
Common Optimization
Optimize for debugging experience rather than speed or size

die alle OOptionen angeben . Beachten Sie, wie -O<n>in einer von der anderen Familie getrennt ist Os, Ofastund Og.

Beim Erstellen wird eine options.hDatei generiert, die Folgendes enthält:

OPT_O = 139,                               /* -O */
OPT_Ofast = 140,                           /* -Ofast */
OPT_Og = 141,                              /* -Og */
OPT_Os = 142,                              /* -Os */

Als Bonus bemerken wir , während wir nach \bO\ninnen greifen common.opt, die Zeilen:

-optimize
Common Alias(O)

Das lehrt uns, dass --optimize(doppelter Bindestrich, da er mit einem Bindestrich -optimizein der .optDatei beginnt ) ein undokumentierter Alias ​​ist, für -Oden verwendet werden kann als --optimize=3!

Wo OPT_O verwendet wird

Jetzt grep wir:

git grep -E '\bOPT_O\b'

was uns auf zwei Dateien verweist:

Lassen Sie uns zuerst aufspüren opts.c

opts.c: default_options_optimization

Alle opts.cVerwendungen erfolgen innerhalb : default_options_optimization.

Wir greifen nach dem Backtrack, um zu sehen, wer diese Funktion aufruft, und wir sehen, dass der einzige Codepfad ist:

  • main.c:main
  • toplev.c:toplev::main
  • opts-global.c:decode_opts
  • opts.c:default_options_optimization

und main.cist der Einstiegspunkt von cc1. Gut!

Der erste Teil dieser Funktion:

  • tut, integral_argumentwas atoidie Zeichenfolge aufruft, die OPT_Odem Analysieren des Eingabearguments entspricht
  • speichert den Wert darin, opts->x_optimizewo a optsist struct gcc_opts.

struct gcc_opts

Nachdem wir vergeblich gegriffen haben, stellen wir fest, dass dies structauch generiert wird bei options.h:

struct gcc_options {
    int x_optimize;
    [...]
}

wo x_optimizekommt aus den Zeilen:

Variable
int optimize

vorhanden in common.optund dass options.c:

struct gcc_options global_options;

Wir vermuten also, dass dies den gesamten globalen Konfigurationsstatus und int x_optimizeden Optimierungswert enthält.

255 ist ein internes Maximum

in opts.c:integral_argument, atoiwird auf das Eingabeargument angewendet, ebenso INT_MAXeine Obergrenze. Und wenn Sie etwas Größeres hinzufügen, scheint es, dass GCC C undefiniertes Verhalten ausführt. Autsch?

integral_argumentAußerdem wird atoidas Argument dünn umbrochen und zurückgewiesen, wenn ein Zeichen keine Ziffer ist. Negative Werte scheitern also anmutig.

Zurück zu sehen opts.c:default_options_optimizationwir die Linie:

if ((unsigned int) opts->x_optimize > 255)
  opts->x_optimize = 255;

so dass die Optimierungsstufe auf abgeschnitten wird 255. Beim Lesen war opth-gen.awkich auf Folgendes gestoßen:

# All of the optimization switches gathered together so they can be saved and restored.
# This will allow attribute((cold)) to turn on space optimization.

und auf dem generierten options.h:

struct GTY(()) cl_optimization
{
  unsigned char x_optimize;

Das erklärt, warum die Kürzung: Die Optionen müssen auch weitergeleitet werden cl_optimization, wodurch ein charPlatz spart. 255 ist also tatsächlich ein internes Maximum.

opts.c: vielleicht_default_options

Zurück zu opts.c:default_options_optimization, wir stoßen auf das, maybe_default_optionswas interessant klingt. Wir betreten es und maybe_default_optionerreichen dann einen großen Schalter:

switch (default_opt->levels)
  {

  [...]

  case OPT_LEVELS_1_PLUS:
    enabled = (level >= 1);
    break;

  [...]

  case OPT_LEVELS_3_PLUS:
    enabled = (level >= 3);
    break;

Es gibt keine >= 4Überprüfungen, was darauf hinweist, dass dies 3die größtmögliche ist.

Dann suchen wir nach der Definition von OPT_LEVELS_3_PLUSin common-target.h:

enum opt_levels
{
  OPT_LEVELS_NONE, /* No levels (mark end of array).  */
  OPT_LEVELS_ALL, /* All levels (used by targets to disable options
                     enabled in target-independent code).  */
  OPT_LEVELS_0_ONLY, /* -O0 only.  */
  OPT_LEVELS_1_PLUS, /* -O1 and above, including -Os and -Og.  */
  OPT_LEVELS_1_PLUS_SPEED_ONLY, /* -O1 and above, but not -Os or -Og.  */
  OPT_LEVELS_1_PLUS_NOT_DEBUG, /* -O1 and above, but not -Og.  */
  OPT_LEVELS_2_PLUS, /* -O2 and above, including -Os.  */
  OPT_LEVELS_2_PLUS_SPEED_ONLY, /* -O2 and above, but not -Os or -Og.  */
  OPT_LEVELS_3_PLUS, /* -O3 and above.  */
  OPT_LEVELS_3_PLUS_AND_SIZE, /* -O3 and above and -Os.  */
  OPT_LEVELS_SIZE, /* -Os only.  */
  OPT_LEVELS_FAST /* -Ofast only.  */
};

Ha! Dies ist ein starker Indikator dafür, dass es nur 3 Ebenen gibt.

opts.c: default_options_table

opt_levelsist so interessant, dass wir grep OPT_LEVELS_3_PLUSund stoßen auf opts.c:default_options_table:

static const struct default_options default_options_table[] = {
    /* -O1 optimizations.  */
    { OPT_LEVELS_1_PLUS, OPT_fdefer_pop, NULL, 1 },
    [...]

    /* -O3 optimizations.  */
    { OPT_LEVELS_3_PLUS, OPT_ftree_loop_distribute_patterns, NULL, 1 },
    [...]
}

Hier wird also -Ondie in den Dokumenten erwähnte Zuordnung zur spezifischen Optimierung codiert. Nett!

Stellen Sie sicher, dass x_optimize nicht mehr verwendet wird

Die Hauptverwendung von x_optimizebestand darin, andere spezifische Optimierungsoptionen festzulegen, wie sie -fdefer_popauf der Manpage dokumentiert sind. Gibt es noch mehr

Wir grepund finden ein paar mehr. Die Anzahl ist gering, und bei manueller Prüfung stellen wir fest, dass jede Verwendung höchstens a bewirkt x_optimize >= 3, so dass unsere Schlussfolgerung gilt.

lto-wrapper.c

Jetzt gehen wir zum zweiten Vorkommen von OPT_O, das in war lto-wrapper.c.

LTO bedeutet Link Time Optimization, für die, wie der Name schon sagt, eine -OOption erforderlich ist und mit der verknüpft wird collec2(was im Grunde genommen ein Linker ist).

In der Tat lto-wrapper.csagt die erste Zeile von :

/* Wrapper to call lto.  Used by collect2 and the linker plugin.

In dieser Datei OPT_Oscheinen die Vorkommen nur den Wert von Ozu normalisieren , um ihn weiterzuleiten, also sollten wir in Ordnung sein.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
38

Sieben verschiedene Ebenen:

  • -O0 (Standard): Keine Optimierung.

  • -Ooder -O1(dasselbe): Optimieren, aber nicht zu viel Zeit verbringen.

  • -O2: Aggressiver optimieren

  • -O3: Am aggressivsten optimieren

  • -Ofast: Entspricht -O3 -ffast-math. -ffast-mathlöst nicht standardkonforme Gleitkommaoptimierungen aus. Auf diese Weise kann der Compiler vorgeben, dass Gleitkommazahlen unendlich genau sind und dass die Algebra auf ihnen den Standardregeln der reellen Zahlenalgebra folgt. Außerdem wird der Compiler angewiesen, die Hardware anzuweisen, Denormals auf Null zu setzen und Denormals zumindest auf einigen Prozessoren, einschließlich x86 und x86-64, als Null zu behandeln. Denormale lösen bei vielen FPUs einen langsamen Pfad aus. Wenn Sie sie daher als Null behandeln (was den langsamen Pfad nicht auslöst), kann dies einen großen Leistungsgewinn bedeuten.

  • -Os: Für Codegröße optimieren. Dies kann in einigen Fällen die Geschwindigkeit aufgrund eines besseren I-Cache-Verhaltens verbessern.

  • -Og: Optimieren, aber das Debuggen nicht beeinträchtigen. Dies ermöglicht eine nicht peinliche Leistung für Debug-Builds und soll -O0Debug-Builds ersetzen .

Es gibt auch andere Optionen, die von keiner dieser Optionen aktiviert werden und separat aktiviert werden müssen. Es ist auch möglich, eine Optimierungsoption zu verwenden, jedoch bestimmte durch diese Optimierung aktivierte Flags zu deaktivieren.

Weitere Informationen finden Sie auf der GCC-Website.

Demi
quelle
Obwohl es fair zu den anderen Antworten war, existierten weder -Ofast noch -Og, als diese Antworten geschrieben wurden.
Janneb
Warum -O100kompiliert dann?
Einpoklum
3
@einpoklum, weil GCC alles über -O3 als gleich -O3 behandelt.
Demi
Leider erhalten Sie im Debugger mit -Og immer noch eine Menge <optimiert>. Das Treten springt immer noch zufällig herum. Es ist meiner Meinung nach nutzlos.
Doug65536
3

Vier (0-3): Siehe GCC 4.4.2- Handbuch . Alles, was höher ist, ist nur -O3, aber irgendwann wird die variable Größenbeschränkung überschritten.

Tom
quelle
Ich habe den Quellcode in meiner Antwort untersucht und stimme Ihnen zu. Pedantischer scheint sich GCC auf atoiundefiniertes Verhalten zu stützen , gefolgt von einer 255internen Grenze.
Ciro Santilli 法轮功 冠状 病 六四 事件 18
4
Bitte ziehen Sie in Betracht, Ihre Antwort zu entfernen, da sie (zumindest heutzutage) falsch ist.
Einpoklum