Opções de Otimização do GCC :: Admirável Mundo Novo




Muito Bem Vindo

Prezado Leitor, a proposta desse Blog é compartilhar conhecimento com as pessoas que trabalham com Linux, Asterisk, OpenSER, e com tecnologia de voz sobre a rede IP em geral, através de tutoriais, dicas, howto, notícias entre outros assuntos.

Atente para termo de uso do conteúdo do blog no rodapé da página.

terça-feira, 24 de junho de 2008

Opções de Otimização do GCC

Como contribuição ao pessoal que mexe com desenvolvimento do Asterisk e Linux, e em geral com código fonte aberto, segue abaixo algumas opções de otimização de compilação para o compilador GNU GCC, extraido de "Opções Que Controlam a Otimização" sob o item 3.10 do manual do GCC.

Essas opções controlam vários tipos de otimizações:

-O / -O1

Otimiza. A compilação com otimização toma um pouco mais de tempo, e mais memória para uma função grande. Sem a opção ‘-O’, o objetivo do compilador é reduzir o custo da compilação e gerar mensagens de debug para produzir os resultados esperados. Declarações são independentes: se você parar o programa com um breakpoint entre declarações, você pode então atribuir um novo valor a qualquer variável ou alterar o contador de programa para qualquer outra declaração na função e obter exatamente os resultados que você esperaria do código fonte. Sem a opção ‘-O’, o compilador somente aloca variáveis declaradas como register nos registradores. O código compilado resultante é menos pior do que o código produzido pelo PCC sem a opção ‘-O’. Com a opção ‘-O’, o compilador tenta reduzir o tamanho do código e o tempo de execução. Quando você especifica ‘-O’, o compilador ativa os flags ‘-fthread-jumps’ e ‘-fdefer-pop’ em todas as máquinas. O compilador liga o flag ‘-fdelayed-branch’ nas máquinas que tem slots de atraso, e ‘-fomit-frame-pointer’ nas máquinas que podem suportar o trabalho de debug mesmo sem um apontador de frame. Em algumas máquinas o compilador também liga outros flags.

-O2

Otimiza mais. O GCC executa quase todas as otimizações suportadas que não envolvem um compromisso entre espaço e velocidade. O compilador não executa o desatamento de loop e nem executa o inline de função quando você especifica ‘-O2’. Quando comparado a ‘-O’, essa opção aumenta tanto o tempo de compilação quanto a performance do código gerado. A opção ‘-O2’ ativa todas as otimizações opcionais exceto o desatamento de loop, fazer inline de função, e a renomeação de registrador. Ela também ativa a opção ‘-fforce-mem’ em todas as máquinas e a eliminação de ponteiro de frame em máquinas onde trabalham dessa forma e não interfere no trabalho de debug.

-O3

Otimiza ainda mais. A opção ‘-O3’ liga todas as otimizações que são especificadas com a opção ‘-O2’ e, também essa opção ativa os flags ‘-finline-functions’ e ‘-frename-registers’.

-O0

Não otimiza.

-Os

Otimiza tamanho. A opção ‘-Os’ habilita todas as otimizações ‘-O2' que não aumenta tipicamente o tamanho do código. Ele também executa as demais otimizações projetadas para reduzir o tamanho do código. Se você usar múltiplas opções ‘-O’, com ou sem números de nível, a última de tais opções é aquela que é considerada. Opções da forma ‘-fflag’ especificam os flags independentes de máquina. Muitos flags possuem ambas as formas positiva e negativa; a forma negativa de ‘-ffoo’ seria ‘-fno-foo’. Na tabela abaixo, somente uma das formas é listada – aquela que não estiver presente é a opção padrão. Você pode inferir que a outra forma será removendo o prefixo ‘no-' ou adicionando-o.

-ffloat-store

Não armazena variáveis de ponto flutuante em registradores, e inibe outras opções que podem ser alteradas se um valor de ponto flutuante for tomando de um registrador ou memória. Essa opção evita excesso de precisão indesejável em máquinas como a 68000 onde os registradores de ponto flutuante (do processador 68881) mantêm mais precisão do que uma precisão dupla é assumida ter. Similarmente, o mesmo acontece para a arquitetura x86. Para muitos programas, o excesso de precisão funciona somente bem, mas alguns programas contam com a definição de precisão de ponto flutuante do IEEE. Usa o flag ‘-ffloat-store’ para tais programas, depois de modificá-los para armazenar todas as computações intermediárias pertinentes em variáveis.

-fno-default-inline

Não faz o inline de membros de funções por padrão meramente porque elas são definidas dentro do escopo de classe (somente no C++). Do contrário, quando você especifica a opção ‘-O’, membros de funções definidos dentro do escopo de classe já são compilados com o mecanismo inline por padrão; ou seja, você não precisa adicionar ‘inline’ na frente do nome de membro de função.

-fno-defer-pop

Sempre retira os argumentos para cada chamada de função tão logo essa função retorne. Para máquinas que precisa retirar argumentos depois de uma chamada de função, o compilador normalmente deixa argumentos acumulado na pilha para varias chamadas de função e os retiram todos de vez.

-fforce-mem

Força os operandos de memória ser copiados em registradores antes de fazer operações aritméticas com eles. Isso produz melhor código porque faz todas as referências de memória de sub-expressões potenciais comum. Quando elas não são sub-expressões comuns, a combinação de instrução deve eliminar a carga de registrador separado. A opção ‘-O2’ ativa essa opção.

-fforce-addr

Força as constantes de endereço de memória sejam copiadas nos registradores antes de fazer operações aritméticas com elas. Isso pode produzir melhor código apenas quando o flag ‘-fforce-mem’ puder ser definido.

-fomit-frame-pointer

Não guarda o ponteiro de frame em um registrador para funções que não precisam dele. Isso evita as instruções de salvar, definir e recuperar ponteiros de frame; ele também torna disponível um registrador extra em muitas funções. Ele também torna impossível fazer debugs em algumas máquinas. Em algumas máquinas, como a Vax, esse flag não tem qualquer efeito, porque a seqüência de chamada padrão automaticamente manipula o ponteiro de frame e algo que não existe não é salvo em tese. A macro de descrição de máquina FRAME_POINTER_REQUIRED controla se uma máquina em questão suporta esse flag. Veja a seção 21.6 Uso de Registrador.

-foptimize-sibling-calls

Otimiza chamadas irmãs e chamadas com prolongamento recursivo.

-ftrapv

Essa opção gera traps de transbordamento sinalizado nas operações de adição, subtração e multiplicação.

-fno-inline

Não dá atenção à palavra chave inline. Normalmente essa opção é usada para prevenir o compilador de expandir quaisquer funções marcadas para executar o inline dela. Note que se você não estiver otimizando, nenhuma função pode ser expandida pela ação do inline.

-finline-functions

Integra todas as funções simples dentro de suas funções chamantes. O compilador decide heuristicamente quais são as funções suficientemente simples para valer a pena fazer a integração assim. Se todas as chamadas a certa função forem integradas, e a função for declarada estática, então a função normalmente não vai gerar saída na forma de código Assembly por ela mesma.

-finline-limit=n

Por padrão, o GCC limita o tamanho de funções que pode ser feito o inline. Esse flag permite o controle desse limite para funções que são explicitamente marcadas como inline (ou seja, marcadas com a palavra chave inline ou definida dentro da definição de classe no C++). O valor n é o tamanho de funções que podem ser inline-ada em número de pseudo-instruções (não contando com o parâmetro handling). O valor padrão de n é 10.000. Aumentando esse valor pode resultar em mais código inline-ado ao custo de tempo de compilação e consumo de memória. Diminuindo normalmente torna a mais rápida a compilação e menos código será inline-ado (que presumivelmente significa programas mais lento). Essa opção é particularmente útil para programas que usam o inline-mento intensamente como aqueles baseados nos modelos recursivos do C++. Obs.: pseudo-instruções representam, nesse contexto particular, uma medida abstrata de tamanho de função. Em hipótese alguma, elas representam uma contagem de instruções Assembly e como tal seu significado exato pode mudar de uma liberação para outra.

-fkeep-inline-functions

Mesmo que todas as chamadas a certa função seja integrada, e a função seja declarada estática, no entanto, a saída será uma versão da função acessível separada no tempo de execução. Essa troca não afeta o inline de funções externas.

-fkeep-static-consts

Deixa passar variáveis const declaradas como static quando a otimização não estiver ativada, mesmo quando as variáveis não sejam referenciadas. O compilador GCC habilita essa opção por padrão. Se você deseja forçar o compilador a verificar se a variável foi referenciada, sem importar se otimização está ou não ativada, use a opção ‘-fno-keep-static-consts’.

-fno-function-cse

Não coloca endereços de função em registradores; faz que cada instrução que chama uma função constante contenha explicitamente o endereço da função. Essa opção resulta em código menos eficiente, mas alguns hacks estranhos que altera a saída do assembly possam ser confundidos pelas otimizações executadas quando essa opção não for usada.

-ffast-math

Essa opção permite o GCC violar algumas regras e/ou especificações ISO ou IEEE do interesse do código de otimização para velocidade. Por exemplo, ela permite ao compilador assumir argumentos para a função sqrt seja números não-negativos e que nenhum valor de ponto flutuante sejam NaNs. Essa opção faz que a macro do pré-processador __FAST_MATH__ seja definida. Essa opção nunca deve ser ativada em qualquer opção ‘-O’ já que ela pode resultar em saída incorreta para programas que dependem de uma implementação exata das regras/especificações do IEEE ou ISO para funções matemáticas.

-fno-math-errno

Não define ERRNO apos a chamada a funções matemáticas que são executadas com uma instrução única, por exemplo, sqrt. Um programa que espera exceções IEEE para tratamento de erro matemático pode desejar usar esse flag para velocidade enquanto mantém compatibilidade aritmética com a IEEE. O padrão é ‘-fmath-errno’. As opções ‘-ffast-math’ define ‘-fno-math-errno’. As opções seguintes controlam as otimizações específicas. A opção ‘-O2’ ativa todas essas otimizações exceto ‘-funroll-loops’ e ‘-funroll-all-loops’. Em muitas máquinas, a opção ‘-O’ ativa as opções ‘-fthread-jumps’ e ‘-fdelayed-branch’, mas máquinas específicas podem manipulá-los diferentemente. Você pode usar os flags seguintes nos casos raros quando do "ajuste fino" de otimizações para ser executado é desejado.

-fstrength-reduce

Executa as otimizações de redução fortes de loop e a eliminação de variáveis de interação.

-fthread-jumps

Executa otimizações onde verificamos pra ver se um salto desvia para um ponto onde uma outra comparação incorporada pelo primeiro for encontrada. Se assim for, o primeiro desvio será redirecionado tanto para o destino do segundo desvio ou um ponto imediatamente seguinte a ele, dependendo se a condição é sabida ser verdadeira ou falsa.

-fcse-follow-jumps

Na eliminação de sub-expressão comum, executa varredura através de instruções jump quando o destino do salto não for alcançado por nenhum outro caminho. Por exemplo, quando CSE encontra uma declaração if com uma cláusula else, o CSE seguirá o salto quando a condição testada for falsa.

-fcse-skip-blocks

Essa é similar a ‘-fcse-follow-jumps’, mas faz o CSE seguir saltos que pula condicionalmente além dos blocos. Quando CSE encontra uma simples declaração if sem cláusula else, o flag ‘-fcse-skip-blocks’ faz o CSE seguir o salto ao longo do corpo do if.

-frerun-cse-after-loop

Re-executa eliminação de sub-expressão comum depois que otimizações de loop tenham sido executadas.

-frerun-loop-opt

Executa o otimizador de loop duas vezes.

-fgcse

Executa uma passagem de eliminação global de sub-expressão comum. Essa passagem também executa constante global e propagação de cópia.

-fdelete-null-pointer-checks

Usa análise de fluxo de dados global para identificar e eliminar verificações inúteis de ponteiro nulo. Programas que contam com o ponteiro NULL que não fazem de-referência de parada do programa não pode funcionar apropriadamente com essa opção. Use o flag ‘-fno-delete-null-pointer-checks’ para desabilitar essa otimização para programas que dependem desse comportamento.

-fexpensive-optimizations

Executa um número de otimizações menores que são relativamente cara.

-foptimize-register-move / -fregmove

Tenta re-atribuir números de registradores em instruções move e como operando de outras instruções simples para maximizar a quantidade de amarração de registradores. Isso é especialmente útil em máquinas com instruções de dois operandos. O compilador GCC habilita essa otimização por padrão com a opção ‘-O2’ ou superior. Observe que ‘-fregmove’ e ‘-foptimize-register-move’ é a mesma otimização.

-fdelayed-branch

Se for suportado pela máquina em tela, tenta reordenar instruções para explorar slots de instruções disponíveis depois de instruções de desvios atrasadas.

-fschedule-insns

Se for suportado pela máquina alvo, tenta reordenar instruções para eliminar execução instável devido a dados necessários estar indisponível. Isso ajuda máquinas que possui instruções de ponto flutuante ou de carga de memória lentas permitindo que outras instruções sejam lançadas até o resultado da instrução de carga ou de ponto flutuante seja exigido.

-fschedule-insns2

Similar ao flag ‘-fschedule-insns’, mas solicita um passo adicional do escalonamento de instrução depois que a alocação de registrador tenha sido feita. Essa é especialmente útil em maquinas com um número relativamente pequeno de registradores e onde instruções de carga na memória tomam mais de um ciclo.

-ffunction-sections / -fdata-sections

Coloca cada função ou item de dados dentro de sua própria seção no arquivo saída se o alvo suporta seções arbitrarias. O nome da função ou o nome do item de dados determina o nome da seção no arquivo saída. Use essas opções em sistemas onde o linkador pode executar otimizações para melhorar localidade de referência no espaço de instrução. Os processadores HPPA rodando o HP-UX e os processadores Sparc rodando o Solaris 2 possuem linkadores com tais otimizações. Outros sistemas que usam o formato de objeto ELF bem como o AIX podem ter essas otimizações no futuro. Use somente essas opções quando existem benefícios significantes de fazer isso. Quando você especifica essas opções, o compilador assembly e o linkador criarão arquivos objetos e executáveis grandes e serão também mais lentos. Você não será capaz de usar gprof em todos os sistemas se você especificar essa opção e você pode ter problemas com o trabalho de debug se você especificar tanto essa opção como a opção ‘-g’.

-fcaller-saves

Permite que valores sejam alocados nos registradores que será pego pelas chamadas de função, by emitting instruções extras salvar e recuperar os registradores em torno de tais chamadas. Tal alocação é feita somente quando ela parece resultar em código melhor do que seria produzido do contrário. Essa opção é sempre habilitada por padrão em certas máquinas, normalmente aquelas que tenham nenhum registrador preservado de chamado usar no lugar. Em todas as maquinas, otimização nível 2 e superior habilita esse flag por padrão.

-funroll-loops

Executa a otimização de desatamento de loop. Isso é feito somente para loops cujo número de interações pode ser determinado no tempo de compilação ou no tempo de execução. A opção ‘-funroll-loops’ implica nas opções ‘-fstrength-reduce’ e ‘-frerun-cse-after-loop’.

-funroll-all-loops

Executa a otimização de desatamento de loop. Isso é feito para todos os loops e normalmente faz os programas rodarem mais lentamente. O flag ‘-funroll-all-loops’ implica na opção ‘-fstrength-reduce’ bem como na opção ‘-frerun-cse-after-loop’.

-fmove-all-movables

Força que todas as computações invariantes de loops sejam movidas para fora do loop.

-freduce-all-givs

Força todas variáveis de indução geral em loops sejam reduzida fortemente. Obs: Quando compilando programas escritos em Fortran, as opções ‘-fmove-all-movables’ e ‘-freduce-all-givs’ são habilitada por padrão quando você usa o otimizador. Essas opções podem gerar código melhor ou pior; os resultados são altamente dependentes da estrutura dos loops dentro do código fonte. Essas duas opções são pensadas para ser removida algum dia, uma vez que elas têm ajudado determinar a eficácia de varias abordagens para melhorar as otimizações de loop. Favor deixe-nos (gcc@gcc.gnu.org e fortran@gnu.org) saber como o uso dessas opções afeta a performance do seu código em produção. We're very interested in code that runs slower when these options are enabled.

-fno-peephole / -fno-peephole2

Desabilita quaisquer otimizações finas específicas de máquina. A diferença entre as opções ‘-fno-peephole’ e ‘-fno-peephole2’ está em como elas são implementadas no compilador; algumas targets usam uma, algumas usam a outra e algumas poucas usam ambas.

-fbranch-probabilities

Após executar um programa compilado com a opção ‘-fprofile-arcs’ (veja a seção Options for Debugging Your Program ou GCC), você pode compilá-lo uma segunda vez usando a opção ‘-fbranch-probabilities’, para melhorar otimizações baseadas na suposição do caminho que um desvio pode tomar. Com a opção ‘-fbranch-probabilities’, o GCC coloca uma observação ‘REG_EXEC_COUNT’ na primeira instrução de cada bloco básico, e uma observação ‘REG_BR_PROB’ em cada ‘JUMP_INSN’ e ‘CALL_INSN’. Essas podem ser usadas para melhorar a otimização. Atualmente, elas são usadas somente em um lugar: no arquivo ‘reorg.c’, em vez de imaginar qual caminho um desvio é tomado mais frequentemente, os valores ‘REG_BR_PROB’ são usados para determinar exatamente qual o caminho é tomado como mais freqüência.

-fno-guess-branch-probability

Às vezes o GCC vai optar por supor probabilidades de desvio quando nenhum estiver disponível tanto de feedback de traço direcionado (‘-fprofile-arcs’) quanto de ‘__builtin_expect’. Em um sistema pesado de tempo real, as pessoas não desejam execuções diferentes do compilador para produzir código que tenha comportamento diferente; minimizar o não determinismo é de importância suprema. Essa troca permite aos usuários reduzir o não determinismo, possivelmente ao custo de otimização inferior.

-fstrict-aliasing

Permite ao compilador supor as regras de mais strict aliasing (torna ilegal que ponteiros de tipos diferentes referenciem a mesma posição de memória) aplicáveis à linguagem que está sendo compilada. Para o C (e C++), isso ativa otimizações baseadas no tipo de expressões. Em particular, um objeto de um tipo é assumido nunca residir no mesmo endereço que um objeto de um outro tipo, a não ser que os tipos sejam quase o mesmo. Por exemplo, um int sem sinal pode alias um int, mas não um tipo void* ou um tipo double. Um tipo caractere pode alias qualquer outro tipo. Preste atenção especial ao código como esse:

union a_union {

int i;

double d;

};

int f() {

a_union t;

t.d = 3.0;

return t.i;

}

A prática de leitura de membro de uma union diferente daquela escrita mais recentemente (chamada "type-punning") é comum. Mesmo com o flag ‘-fstrict-aliasing’, o trocadilho de tipo é permitido, fornecido a memória é acessada através do tipo union. Assim, o código acima funcionará como esperado. No entanto, o código a seguir pode não funcionar:

int f() {

a_union t;

int* ip;

t.d = 3.0;

ip = &t.i;

return *ip;

}

Toda linguagem que deseja executar análises de alias específico de linguagem deve definir uma função que compute, dado um Nó de arvore, um conjunto alias para o Nó. Nós em conjuntos de alias diferentes não são permitidos fazer alias. Como um exemplo, veja a função C de front-end c_get_alias_set.

-falign-functions / -falign-functions=n

Alinha o inicio de funções a próxima potência de 2 maior do que n, pulando até n bytes. Por exemplo, a opção ‘-falign-functions=32’ alinha funções ao limite próximo de 32 bytes, mas o flag ‘-falign-functions=24’ alinharia ao limite próximo de 32 bytes somente se isso pode ser feito saltando 23 bytes ou menos. Os flags ‘-fno-align-functions’ e ‘-falign-functions=1’ são equivalentes e significa que funções não serão alinhadas. Alguns compiladores Assemblers suportam somente esse flag quando n for uma potencia de dois; nesse caso, será arredondado para mais. Se n não for especificado, use um padrão dependente de máquina.

-falign-labels / -falign-labels=n

Alinha todos os destinos de desvio para uma fronteira potencia de dois, saltando até n bytes como ‘-falign-functions’. Essa opção pode facilmente tornar mais lento o código, porque ele precisa inserir operações dummy quando o destino de desvio for alcançado no fluxo usual do código. Se o flag ‘-falign-loops’ ou o ‘-falign-jumps’ forem aplicáveis e forem maiores do que esse valor, então seus valores serão usados no lugar. Se n não for especificado, usa um padrão dependente de máquina que será muito provavelmente igual a ‘1’, o que significa nenhum alinhamento.

-falign-loops / -falign-loops=n

Alinha loops para um limite de potência de dois, saltando até n bytes como o flag ‘-falign-functions’. A esperança é que o loop será executado várias vezes, o que compensará alguma execução das operações dummy. Se n não for especificado, usa um padrão dependente de máquina.

-falign-jumps / -falign-jumps=n

Alinha destinos de desvio para um limite potência de dois, para destinos de desvio onde os destinos podem ser alcançados somente por salto, pulando até n bytes como o flag ‘-falign-functions’. Nesse caso, nenhumas operações dummy precisam ser executadas. Se n não for especificado, usa um padrão dependente de máquina.

-fssa

Executa otimizações na forma de atribuição de static simples. Cada gráfico de fluxo da função é traduzido na forma SSA, otimizações são executadas, e o gráfico de fluxo é traduzido de volta para a forma SSA. Usuários não devem especificar essa opção, uma vez que essa opção não está ainda pronta para uso em produção.

-fdce

Executa eliminação de código morto na forma SSA. Requer que o flag ‘-fssa’. Como o flag ‘-fssa’, essa funcionalidade é uma opção experimental.

-fsingle-precision-constant

Trata constante de ponto flutuante como constante de precisão simples em vez de implicitamente convertê-la em constante de precisão dupla.

-frename-registers

Tenta evitar dependências falsas no código escalonado fazendo uso de registradores deixado de lado após alocação de registrador. Essa otimização beneficiará muitos processadores com bastantes registradores. Isso pode, contudo, tornar impossível o trabalho de debug, já que as variáveis não mais permanecerão em um "registrador home".

--param name=value

Em alguns lugares, o GCC usa várias constantes para controlar a quantidade de otimização que é feita. Por exemplo, o GCC não fará o inline de funções que contenham mais que certo número de instruções. Você pode controlar algumas dessas constantes na linha de comando usando a opção ‘--param’. Em cada caso, o valor é um inteiro. As escolhas permissíveis para nome são dadas na tabela seguinte:

max-delay-slot-insn-search

O número máximo de instruções a considerar quando buscado uma instrução para preencher um slot de atraso. Se for maior do que esse número arbitrário de instruções for pesquisado, a economia de tempo de preenchimento de slot de atraso será mínima de modo a parar a pesquisa. Aumento de valores significa mais otimização agressiva, fazendo o tempo de compilação maior provavelmente com pequeno melhoramento no tempo de execução do executável.

max-delay-slot-live-search

Quanto tentando preencher slots de atraso, o número máximo de instruções a considerar quando pesquisando um bloco com informação de registrador vivo válido. Aumentando esse valor escolhido arbitrariamente significa mais otimização agressiva, aumentando o tempo de compilação. Esse parâmetro deve ser removido quando o código de slot de atraso for reescrito para manter o gráfico de fluxo de controle.

max-gcse-memory

A quantidade máxima de memória aproximada que será alocada para executar otimização de eliminação global de sub-expressão comum. Se mais memória do que a especificada for requerida, a otimização não será feita.

max-inline-insns

Se uma função contém muito mais dessas instruções, não será feito o inline. Essa opção é precisamente equivalente a ‘-finline-limit’.

Um comentário:

Anônimo disse...

Excelente artigo! Parabéns !




Creative Commons License
Admirável Mundo Novo: Tudo Sobre Asterisk, OpenSER, Linux e Tecnologias de Voz sobre IP
by Cléviton Mendes de Araújo is licensed under a Creative Commons Atribuição 2.5 Brasil License.