Quantos níveis de otimização GCC existem?

101

Quantos GCC níveis de otimização existem?

Tentei gcc -O1, gcc -O2, gcc -O3 e gcc -O4

Se eu usar um número muito grande, não funcionará.

No entanto, eu tentei

gcc -O100

e compilado.

Quantos níveis de otimização existem?

neuromancer
fonte
13
@minitech Qual FM você está olhando? Mesmo com o man gccCygwin (12.000 linhas ímpares), você pode pesquisar -Oe encontrar tudo o que as respostas abaixo afirmam e muito mais.
Jens
1
@minmaxavg depois de ler a fonte, discordo de você: qualquer coisa maior que 3é igual a 3(desde que não inttransborde). Veja minha resposta .
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
Na verdade, o GCC tem muitos outros sinalizadores para ajustar as otimizações. -fomit-stack-pointer irá alterar o código gerado.
Basile Starynkevitch

Respostas:

141

Para ser pedante, existem 8 opções -O válidas diferentes que você pode dar ao gcc, embora haja algumas que significam a mesma coisa.

A versão original desta resposta afirmava que havia 7 opções. Desde então, o GCC aumentou -Ogpara trazer o total para 8

A partir da página do homem:

  • -O (O mesmo que -O1)
  • -O0 (sem otimização, o padrão se nenhum nível de otimização for especificado)
  • -O1 (otimizar minimamente)
  • -O2 (otimizar mais)
  • -O3 (otimizar ainda mais)
  • -Ofast (otimizar muito agressivamente ao ponto de quebrar a conformidade padrão)
  • -Og (Otimize a experiência de depuração. -Og permite otimizações que não interferem na depuração. Deve ser o nível de otimização de escolha para o ciclo padrão de edição-compilação-depuração, oferecendo um nível razoável de otimização, mantendo a compilação rápida e uma boa experiência de depuração. )
  • -Os(Otimizar para tamanho. -OsAtiva todas as -O2otimizações que normalmente não aumentam o tamanho do código. Também executa otimizações adicionais projetadas para reduzir o tamanho do código. -OsDesativa os seguintes sinalizadores de otimização:-falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -freorder-blocks-and-partition -fprefetch-loop-arrays -ftree-vect-loop-version )

Também pode haver otimizações específicas da plataforma, como @pauldoo observa, o OS X tem -Oz

Glen
fonte
23
Se você estiver desenvolvendo no Mac OS X, há uma -Ozconfiguração adicional que é "otimizar para o tamanho de forma mais agressiva do que -Os": developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/…
pauldoo
6
Nota: O3 não é necessariamente melhor do que O2, mesmo se o nome sugerir. Experimente ambos.
johan d
1
página @pauldoo 404, substitua por archive.org
noɥʇʎԀʎzɐɹƆ
Há também -Og, que são todas as opções de otimização que não interferem na depuração
einpoklum
47

Vamos interpretar o código-fonte do GCC 5.1 para ver o que acontece, -O100pois não está claro na página do manual.

Devemos concluir que:

  • qualquer coisa acima -O3até INT_MAXé igual -O3, mas isso pode mudar facilmente no futuro, então não confie nisso.
  • O GCC 5.1 executa um comportamento indefinido se você inserir números inteiros maiores que INT_MAX.
  • o argumento pode ter apenas dígitos ou falha normalmente. Em particular, isso exclui números inteiros negativos como-O-1

Foco em subprogramas

Primeiro lembre-se que GCC é apenas um-front-end para cpp, as, cc1, collect2. Um rápido ./XXX --helpdiz isso apenas collect2e cc1pegue -O, então vamos nos concentrar neles.

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.

então -Ofoi encaminhado para ambos cc1e collect2.

O em comum.opt

common.opt é um formato de descrição de opção CLI específico do GCC descrito na documentação interna e traduzido para C por opth-gen.awk e optc-gen.awk .

Ele contém as seguintes linhas interessantes:

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

que especificam todas as Oopções. Observe como -O<n>está em uma família separada da outra Os, Ofaste Og.

Quando construímos, isso gera um options.harquivo que contém:

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

Como um bônus, enquanto procuramos por \bO\ndentro common.opt, notamos as linhas:

-optimize
Common Alias(O)

que nos ensina que --optimize(traço duplo porque começa com um traço -optimizeno .optarquivo) é um apelido não documentado para o -Oqual pode ser usado como --optimize=3!

Onde OPT_O é usado

Agora nós grep:

git grep -E '\bOPT_O\b'

que nos aponta para dois arquivos:

Vamos primeiro rastrear opts.c

opts.c: default_options_optimization

Todos os opts.cusos acontecer dentro: default_options_optimization.

Retrocedemos para ver quem chama esta função e vemos que o único caminho de código é:

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

e main.cé o ponto de entrada de cc1. Boa!

A primeira parte desta função:

  • faz o integral_argumentque chama atoia string correspondente a OPT_Opara analisar o argumento de entrada
  • armazena o valor dentro de opts->x_optimizeonde optsé a struct gcc_opts.

struct gcc_opts

Depois de fazer grep em vão, notamos que isso structtambém é gerado em options.h:

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

de onde x_optimizevem as linhas:

Variable
int optimize

presente em common.opt, e que options.c:

struct gcc_options global_options;

então achamos que é isso que contém todo o estado global da configuração, e int x_optimize é o valor de otimização.

255 é um máximo interno

in opts.c:integral_argument, atoié aplicado ao argumento de entrada, assim INT_MAXcomo um limite superior. E se você colocar algo maior, parece que o GCC executa um comportamento C indefinido. Ai?

integral_argumenttambém envolve atoie rejeita o argumento se algum caractere não for um dígito. Portanto, os valores negativos falham normalmente.

Voltar para opts.c:default_options_optimization, vemos a linha:

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

para que o nível de otimização seja truncado para 255. Durante a leitura opth-gen.awk, descobri:

# 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 no gerado options.h:

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

o que explica por que o truncamento: as opções também devem ser encaminhadas para cl_optimization, que usa umchar para economizar espaço. Portanto, 255 é um máximo interno, na verdade.

opts.c: Maybe_default_options

Voltar para opts.c:default_options_optimization, encontramos o maybe_default_optionsque parece interessante. Nós entramos nele, e então maybe_default_optionchegamos a uma grande mudança:

switch (default_opt->levels)
  {

  [...]

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

  [...]

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

Não há >= 4verificações, o que indica que 3é o maior possível.

Em seguida, procuramos a definição de OPT_LEVELS_3_PLUSem 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! Este é um forte indicador de que existem apenas 3 níveis.

opts.c: default_options_table

opt_levelsé tão interessante que vimos OPT_LEVELS_3_PLUSe encontramos 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 },
    [...]
}

portanto, é aqui que o -Onmapeamento de otimização específico mencionado na documentação é codificado. Agradável!

Certifique-se de que não haja mais usos para x_optimize

O principal uso de x_optimizeera definir outras opções de otimização específicas, como -fdefer_popdocumentado na página do manual. Existem mais?

Nós grep, e encontraremos mais alguns. O número é pequeno e, após inspeção manual, vemos que todo uso faz apenas no máximo a x_optimize >= 3, portanto, nossa conclusão se mantém.

lto-wrapper.c

Agora vamos para a segunda ocorrência de OPT_O, que foi em lto-wrapper.c.

LTO significa Link Time Optimization, que como o nome sugere vai precisar de uma -Oopção e será vinculado collec2(que é basicamente um linker).

Na verdade, a primeira linha de lto-wrapper.cdiz:

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

Neste arquivo, as OPT_Oocorrências parecem apenas normalizar o valor de Opara repassá-lo, então devemos ficar bem.

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

Sete níveis distintos:

  • -O0 (padrão): Sem otimização.

  • -Oou -O1(mesma coisa): Otimize, mas não gaste muito tempo.

  • -O2: Otimize de forma mais agressiva

  • -O3: Otimize de forma mais agressiva

  • -Ofast: Equivalente a -O3 -ffast-math. -ffast-mathdispara otimizações de ponto flutuante não compatíveis com os padrões. Isso permite ao compilador fingir que os números de ponto flutuante são infinitamente precisos e que a álgebra sobre eles segue as regras padrão da álgebra de números reais. Também diz ao compilador para dizer ao hardware para liberar os denormais para zero e tratar os denormais como zero, pelo menos em alguns processadores, incluindo x86 e x86-64. Os denormais acionam um caminho lento em muitas FPUs e, portanto, tratá-los como zero (o que não aciona o caminho lento) pode ser uma grande vitória de desempenho.

  • -Os: Otimize para o tamanho do código. Isso pode realmente melhorar a velocidade em alguns casos, devido ao melhor comportamento do I-cache.

  • -Og: Otimiza, mas não interfere na depuração. Isso permite um desempenho não constrangedor para compilações de depuração e destina-se a substituir -O0para compilações de depuração.

Existem também outras opções que não são habilitadas por nenhum deles e devem ser habilitadas separadamente. Também é possível usar uma opção de otimização, mas desabilitar sinalizadores específicos habilitados por esta otimização.

Para obter mais informações, consulte o site do GCC.

Demi
fonte
De fato, embora para ser justo com as outras respostas, nem -Ofast nem -Og existiam quando essas respostas foram escritas.
janeiro
Então, por que -O100compila?
einpoklum
3
@einpoklum porque o GCC trata tudo acima de -O3 como igual a -O3.
Demi de
Infelizmente, você ainda obtém uma tonelada de <saída otimizada> no depurador com -Og. Pisando ainda pula aleatoriamente. É IMHO inútil.
doug65536
3

Quatro (0-3): Consulte o manual GCC 4.4.2 . Qualquer coisa maior é apenas -O3, mas em algum ponto você irá estourar o limite de tamanho variável.

Tom
fonte
Eu explorei o código-fonte em minha resposta e concordo com você. Mais pedantemente, o GCC parece depender de atoium comportamento indefinido, seguido por um 255limite interno.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
4
Considere remover sua resposta, pois (pelo menos atualmente) está incorreta.
einpoklum