Quanti livelli di ottimizzazione GCC ci sono?


101

Quanti livelli di ottimizzazione GCC ci sono?

Ho provato gcc -O1, gcc -O2, gcc -O3 e gcc -O4

Se uso un numero molto grande, non funzionerà.

Tuttavia, ho provato

gcc -O100

e ha compilato.

Quanti livelli di ottimizzazione ci sono?


13
@minitech Quale FM stai guardando? Anche con man gccCygwin (12000 righe dispari) puoi cercare -Oe trovare tutto ciò che le risposte sottostanti indicano, e poi alcune.
Jens

1
@minmaxavg dopo aver letto la fonte, non sono d'accordo con te: qualsiasi cosa più grande di 3è uguale a 3(purché non inttrabocchi). Vedi la mia risposta .
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
In realtà, GCC ha molti altri flag per mettere a punto le ottimizzazioni. -fomit-stack-pointer cambierà il codice generato.
Basile Starynkevitch

Risposte:


141

Per essere pedanti, ci sono 8 diverse opzioni -O valide che puoi dare a gcc, anche se ce ne sono alcune che significano la stessa cosa.

La versione originale di questa risposta affermava che c'erano 7 opzioni. Da allora GCC ha aggiunto -Ogper portare il totale a 8

Dalla pagina man:

  • -O (Uguale a -O1)
  • -O0 (non eseguire l'ottimizzazione, l'impostazione predefinita se non viene specificato alcun livello di ottimizzazione)
  • -O1 (ottimizzare al minimo)
  • -O2 (ottimizzare di più)
  • -O3 (ottimizza ancora di più)
  • -Ofast (ottimizzare in modo molto aggressivo fino al punto di infrangere la conformità agli standard)
  • -Og (Ottimizza l'esperienza di debug. -Og consente ottimizzazioni che non interferiscono con il debug. Dovrebbe essere il livello di ottimizzazione di scelta per il ciclo standard di modifica-compilazione-debug, offrendo un livello ragionevole di ottimizzazione pur mantenendo una compilazione veloce e una buona esperienza di debug. )
  • -Os(. Ottimizza per dimensione -Osconsente a tutti -O2ottimizzazioni che non in genere aumentano le dimensioni del codice Svolge, inoltre, ulteriori ottimizzazioni volte a ridurre le dimensioni del codice.. -OsDisattiva i seguenti flag di ottimizzazione: -falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -freorder-blocks-and-partition -fprefetch-loop-arrays -ftree-vect-loop-version)

Potrebbero esserci anche ottimizzazioni specifiche della piattaforma, come osserva @pauldoo, OS X ha -Oz


23
Se stai sviluppando su Mac OS X c'è un'impostazione aggiuntiva -Ozche è "ottimizza per le dimensioni in modo più aggressivo di -Os": developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/…
pauldoo

6
Nota: O3 non è necessariamente migliore di O2 anche se il nome lo suggerisce. Prova entrambi.
johan d

1
Pagina @pauldoo 404, sostituisci con archive.org
noɥʇʎԀʎzɐɹƆ

C'è anche -Og, che è tutte le opzioni di ottimizzazione che non interferiscono con il debug
einpoklum

47

Cerchiamo di interpretare il codice sorgente di GCC 5.1 per vedere cosa succede in -O100quanto non è chiaro nella pagina del manuale.

Concluderemo che:

  • qualsiasi cosa sopra -O3fino a INT_MAXè uguale a -O3, ma potrebbe facilmente cambiare in futuro, quindi non fare affidamento su di esso.
  • GCC 5.1 esegue un comportamento indefinito se inserisci numeri interi maggiori di INT_MAX.
  • l'argomento può contenere solo cifre o non riesce correttamente. In particolare, questo esclude interi negativi come-O-1

Concentrati sui sottoprogrammi

In primo luogo ricordare che GCC è solo un front-end per cpp, as, cc1, collect2. Un rapido ./XXX --helpdice che solo collect2e cc1prendi -O, quindi concentriamoci su di loro.

E:

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

dà:

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.

così è -Ostato inoltrato a entrambi cc1e collect2.

O in comune. Opt

common.opt è un formato di descrizione dell'opzione CLI specifico di GCC descritto nella documentazione interna e tradotto in C da opth-gen.awk e optc-gen.awk .

Contiene le seguenti righe interessanti:

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

che specificano tutte le Oopzioni. Si noti come -O<n>è in una famiglia separata dalle altre Os, Ofaste Og.

Quando creiamo, questo genera un options.hfile che contiene:

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

Come bonus, mentre cerchiamo l' \bO\ninterno common.optnotiamo le linee:

-optimize
Common Alias(O)

che ci insegna che --optimize(doppio trattino perché inizia con un trattino -optimizesul .optfile) è un alias non documentato per il -Oquale può essere usato come --optimize=3!

Dove viene utilizzato OPT_O

Ora grep:

git grep -E '\bOPT_O\b'

che ci indica due file:

Prima rintracciamo opts.c

opts.c: default_options_optimization

Tutti gli opts.cusi avvengono all'interno: default_options_optimization.

Facciamo un backtrack per vedere chi chiama questa funzione e vediamo che l'unico percorso del codice è:

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

ed main.cè il punto di ingresso di cc1. Buona!

La prima parte di questa funzione:

  • fa ciò integral_argumentche chiama atoila stringa corrispondente a OPT_Oper analizzare l'argomento di input
  • memorizza il valore all'interno di opts->x_optimizedove optsè un struct gcc_opts.

struct gcc_opts

Dopo aver invano invano, notiamo che questo structviene generato anche in options.h:

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

da dove x_optimizeviene le righe:

Variable
int optimize

presente in common.opt, e che options.c:

struct gcc_options global_options;

quindi supponiamo che questo sia ciò che contiene l'intero stato globale della configurazione, ed int x_optimizeè il valore di ottimizzazione.

255 è un massimo interno

in opts.c:integral_argument, atoiviene applicato all'argomento di input, quindi INT_MAXè un limite superiore. E se metti qualcosa di più grande, sembra che GCC esegua un comportamento C indefinito. Ahia?

integral_argumentinoltre racchiude in modo sottile atoie rifiuta l'argomento se un carattere non è una cifra. Quindi i valori negativi falliscono con grazia.

Torna a opts.c:default_options_optimization, vediamo la linea:

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

in modo che il livello di ottimizzazione venga troncato a 255. Durante la lettura opth-gen.awkmi ero imbattuto:

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

e sul generato options.h:

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

il che spiega il motivo del troncamento: le opzioni devono essere inoltrate anche a cl_optimization, che utilizza a charper risparmiare spazio. Quindi 255 è un massimo interno in realtà.

opts.c: forse_default_options

Tornando a opts.c:default_options_optimization, ci imbattiamo in ciò maybe_default_optionsche sembra interessante. Lo entriamo, e poi maybe_default_optiondove raggiungiamo un grande interruttore:

switch (default_opt->levels)
  {

  [...]

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

  [...]

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

Non ci sono >= 4controlli, il che indica che 3è il più grande possibile.

Quindi cerchiamo la definizione di 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.  */
};

Ah! Questo è un forte indicatore che ci sono solo 3 livelli.

opts.c: default_options_table

opt_levelsè così interessante, che grep OPT_LEVELS_3_PLUS, e ci imbattiamo 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 },
    [...]
}

quindi è qui che -Onviene codificata la mappatura per l'ottimizzazione specifica menzionata nei documenti. Bello!

Assicurati che non ci siano più usi per x_optimize

L'utilizzo principale di x_optimizeera quello di impostare altre opzioni di ottimizzazione specifiche -fdefer_popcome documentato nella pagina man. Ce ne sono altri?

Noi grep, e ne troviamo altri. Il numero è piccolo e dopo un'ispezione manuale vediamo che ogni utilizzo fa solo al massimo a x_optimize >= 3, quindi la nostra conclusione vale.

lto-wrapper.c

Ora passiamo alla seconda occorrenza di OPT_O, che era in lto-wrapper.c.

LTO significa Ottimizzazione del tempo di collegamento, che come suggerisce il nome avrà bisogno di -Oun'opzione e sarà collegata a collec2(che è fondamentalmente un linker).

In effetti, la prima riga di lto-wrapper.cdice:

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

In questo file, le OPT_Ooccorrenze sembrano normalizzare solo il valore di Oper passarlo in avanti, quindi dovremmo stare bene.


38

Sette livelli distinti:

  • -O0 (predefinito): nessuna ottimizzazione.

  • -Ooppure -O1(stessa cosa): ottimizza, ma non spendere troppo tempo.

  • -O2: Ottimizza in modo più aggressivo

  • -O3: Ottimizza in modo più aggressivo

  • -Ofast: Equivalente a -O3 -ffast-math. -ffast-mathattiva ottimizzazioni in virgola mobile non conformi agli standard. Ciò consente al compilatore di fingere che i numeri in virgola mobile siano infinitamente precisi e che l'algebra su di essi segue le regole standard dell'algebra dei numeri reali. Inoltre dice al compilatore di dire all'hardware di svuotare i denormali a zero e di trattare i denormali come zero, almeno su alcuni processori, inclusi x86 e x86-64. I denormali innescano un percorso lento su molte FPU, quindi trattarli come zero (che non innesca il percorso lento) può essere una grande vittoria in termini di prestazioni.

  • -Os: Ottimizza per la dimensione del codice. Questo può effettivamente migliorare la velocità in alcuni casi, grazie a un migliore comportamento dell'I-cache.

  • -Og: Ottimizza, ma non interferisce con il debug. Ciò consente prestazioni non imbarazzanti per le build di debug ed è destinato a sostituire le -O0build di debug.

Esistono anche altre opzioni che non sono abilitate da nessuna di queste e devono essere abilitate separatamente. È anche possibile utilizzare un'opzione di ottimizzazione, ma disabilitare flag specifici abilitati da questa ottimizzazione.

Per ulteriori informazioni, vedere il sito Web di GCC.


In effetti, sebbene per essere onesti con le altre risposte, né -Ofast né -Og esistevano quando quelle risposte furono scritte.
janneb

Allora perché -O100compila allora?
einpoklum

3
@einpoklum perché GCC considera tutto ciò che è superiore a -O3 come uguale a -O3.
Demi

Sfortunatamente, ottieni ancora un sacco di <ottimizzato> nel debugger con -Og. Camminare ancora salta in modo casuale. È inutile IMHO.
doug65536

3

Quattro (0-3): vedere il manuale GCC 4.4.2 . Qualunque cosa più alta è solo -O3, ma a un certo punto supererai il limite di dimensione variabile.


Ho esplorato il codice sorgente nella mia risposta e sono d'accordo con te. In modo più pedante, GCC sembra fare affidamento su un atoicomportamento indefinito, seguito da un 255limite interno.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

4
Considera l'idea di rimuovere la tua risposta, poiché (almeno in questi giorni) non è corretta.
einpoklum
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.