Continuando com as variáveis CFLAGS e CXXFLAGS, seus valores serão passados ao compilador quando formos instalar um programa ou qualquer outra compilação que o Portage tenha que fazer. Assim otimizamos a performance de compilação e consequentemente otimizamos o pacote/programa em nosso sistema.
Isto tudo acontece durante as fases do ebuild de src_compile() e de construção do pacote. Durante a fase de compilação de um determinado pacote o Portage precisa verificar no ebuild correspondente o que está definido no arquivo configure que normalmente já vem com os programas, depois comparar com o perfil de usuário, flags setadas, entre inúmeras outras informações.
Por este e outros motivos é que existe os ebuilds para cada programa disponível para o Gentoo. No momento não entrarei em detalhes sobre os códigos de programação dos ebuilds, fica pra próxima. Após esta verificação o pacote estará pronto para compilação.
O Portage, então, configurará o ambiente de compilação e construção buscando informações nos valores das variáveis CFLAGS, CXXFLAGS, FFLAGS, FCFLAGS e LDFLAGS, caso estejam definidas. Pelo menos as duas primeiras deve estar obrigatoriamente definidas de acordo com a arquitetura do usuário.
Caso contrário, a compilação ficará mais lenta e o pacote compilado perderá performance, pois seria compilado considerando diversas arquiteturas, podendo até forçar o Kernel a utilizar diversos drivers e módulos que não seriam necessários.
Estas variáveis são, comumente, definidas da seguinte forma:
CFLAGS="-march=core2 -O2 -pipe"
CXXFLAGS="${CFLAGS}"
A opção mais importante aqui é -march, pois esta opção informará ao compilador para qual arquitetura (arch por razões históricas) produzir os códigos. Assim o pacote será compilado de acordo com as capacidades da nossa arquitetura, características, instruções etc. Por exemplo, caso a arquitetura seja da família do x86, teríamos suporte para toda a gama de cpu's:
- i386
- i586
- core2
- ivybridge
Só para citar algumas. Mas como saber de forma rápida e fácil? Rodando o seguinte comando:
gcc -c -Q -march=native --help=target
O comando acima produzirá uma saída contendo o tipo da CPU e inúmeras outras informações. Com isto em mãos, coloque a informação na CFLAGS, por ex.:
CFLAGS="-march=athlon64"
Caso a saída do comando não informe o tipo de CPU causando uma informação indeterminada, ou ainda, o usuário tenha dúvidas ou receio do que por, é possível definir apenas:
CFLAGS="-march=native"
Assim o GCC detectará automaticamente o tipo de processador e definirá as flags necessárias para ele. Não use isto para compilar pacotes para arquiteturas diferentes.
O próximo passo é o -O (letra "o" maiúscula, não o número zero). Esta variável controla o nível global de otimização e conforme as opções definidas aqui, fará a compilação tomar mais tempo e mais consumo de memória. Há sete níveis para esta configuração: -O0, -O1, -O2, -O3, -Os, -Og, e -Ofast. Use apenas uma delas na variável CFLAGS. Vamos dar uma olhada em cada nível:
-O0: desabilita toda a otimização de compilação, aliás, este é o valor padrão quando não está configurada. Neste nível o tempo de compilação é reduzido e pode melhorar as informações de debug. Porém algumas aplicações podem não funcionar direito sem uma opção de otimização. Esta não é uma opção recomendada, exceto em caso de informações de depuração.
-O1: O nível mais básico de otimização.Aqui o compilador tentará produzir um código rápido e pequeno, sem tomar muito tempo de compilação. Este nível trará as seguintes flags:
- -fauto-inc-dec
- -fbranch-count-reg
- -fcombine-stack-adjustments
- -fcompare-elim
- -fcprop-registers
- -fdce
- -fdefer-pop
- -fdelayed-branch
- -fdse
- -fforward-propagate
- -fguess-branch-probability
- -fif-conversion2
- -fif-conversion
- -finline-functions-called-once
- -fipa-pure-const
- -fipa-profile
- -fipa-reference
- -fmerge-constants
- -fmove-loop-invariants
- -freorder-blocks
- -fshrink-wrap
- -fsplit-wide-types
- -fssa-backprop
- -fssa-phiopt
- -ftree-bit-ccp
- -ftree-ccp
- -ftree-ch
- -ftree-coalesce-vars
- -ftree-copy-prop
- -ftree-dce
- -ftree-dominator-opts
- -ftree-dse
- -ftree-forwprop
- -ftree-fre
- -ftree-phiprop
- -ftree-sink
- -ftree-slsr
- -ftree-sra
- -ftree-pta
- -ftree-ter
- -funit-at-a-time
-O2: este é o nível recomendado para otimização, a não ser que que o sistema precise de algum caso especial. Neste nível será ativada algumas flags a mais do que aquelas ativadas no -O1. O compilador tentará aumentar a performance do código sem aumentar o tamanho do arquivo e sem tomar muito tempo de compilação. Flags:
- -fthread-jumps
- -falign-functions -falign-jumps
- -falign-loops -falign-labels
- -fcaller-saves
- -fcrossjumping
- -fcse-follow-jumps -fcse-skip-blocks
- -fdelete-null-pointer-checks
- -fdevirtualize -fdevirtualize-speculatively
- -fexpensive-optimizations
- -fgcse -fgcse-lm
- -fhoist-adjacent-loads
- -finline-small-functions
- -findirect-inlining
- -fipa-cp
- -fipa-cp-alignment
- -fipa-sra
- -fipa-icf
- -fisolate-erroneous-paths-dereference
- -flra-remat
- -foptimize-sibling-calls
- -foptimize-strlen
- -fpartial-inlining
- -fpeephole2
- -freorder-blocks-algorithm=stc
- -freorder-blocks-and-partition -freorder-functions
- -frerun-cse-after-loop
- -fsched-interblock -fsched-spec
- -fschedule-insns -fschedule-insns2
- -fstrict-aliasing -fstrict-overflow
- -ftree-builtin-call-dce
- -ftree-switch-conversion -ftree-tail-merge
- -ftree-pre
- -ftree-vrp
- -fipa-ra
-O3: o maior nível de otimização possível. As otimizações deste nível consomem muito tempo e memória, sendo que não há garantia nenhuma de ganho de desempenho e performance. Este nível é conhecido por quebrar muitos pacotes. Portanto seu uso é desencorajado. Flags:
- -finline-functions
- -funswitch-loops
- -fpredictive-commoning
- -fgcse-after-reload
- -ftree-loop-vectorize
- -ftree-loop-distribute-patterns
- -fsplit-paths
- -ftree-slp-vectorize
- -fvect-cost-model
- -ftree-partial-pre
- -fpeel-loops
- -fipa-cp-clone
-Os: neste nível o código é otimizado para não ocupar muito espaço na máquina. Este nível ativa todas as flags do -O2. É indicado para máquinas que possuam pouco espaço em disco e/ou para CPU's que possuam pouca cache. Com esta opção, as seguintes flags serão desativadas:
- -falign-functions
- -falign-jumps
- -falign-loops
- -falign-labels
- -freorder-blocks
- -freorder-blocks-algorithm=stc
- -freorder-blocks-and-partition
- -fprefetch-loop-arrays
-Og: Na versão 4.8 do GCC, foi introduzido este novo nível de otimização geral. Ele aborda a necessidade de rápida compilação e uma experiência de depuração superiores, proporcionando um nível razoável de desempenho de execução. A opção -g simplesmente desabilita otimizações que porventura possam atrapalhar na depuração.
-Ofast: Criado na versão 4.7 do GCC, esta opção ativa todas as flags -O3 mais as flags -ffast-math, -fno-protect-parens, e -fstack-arrays. Esta opção quebra alguns padrões e portanto seu uso não é recomendado.
E por último, mas não menos importante, temos a flag -pipe. Esta flag não irá otimizar o código em nada, mas fará com que o processo de compilação fique mais rápido. Esta flag informa ao compilador para utilizar pipes ao invés de arquivos temporários durante as diferentes fases de compilação. Em sistemas com pouca memória não devemos setar esta flag.
Após isto, nossa CFLAGS deve estar parecida com isto:
CFLAGS="-march=core2 -O2 -pipe"
Legal. Mas ainda tem mais algumas coisinhas que, embora possam ser opcionais, seu uso é encorajado para ganho de performance e otimização de código. A flag -fomit-frame-pointer é muito utilizada e nos ajuda diminuindo o código gerado. A mesma pode ser habilitada em todos os níveis de -O, exceto -O0 (letra e número), entretanto é necessário ativá-la. Vamos pô-la então, mas há um porém: caso necessite de debug, a análise pode se tornar mais difícil que o normal ou até impossível.
CFLAGS="-march=core2 -O2 -pipe -fomit-frame-pointer"
Ok. Nossa CFLAGS está pronta. Há mais algumas flags que podemos utilizar: -msse, -msse2, -msse3, -mmmx, -m3dnow. Entretanto, ao definir a primeira flag (-march), estas serão ativadas por padrão de acordo com o que a arquitetura suporta. Para finalizar nossa otimização, devemos declarar a variável CXXFLAGS (códigos em C++) EXATAMENTE como a CFLAGS, eu escrevi exatamente, pois a não ser que você seja desenvolvedor e queira declarar outras flags para códigos C++, ambas as variáveis devem ser iguais ou você estará correndo um grande risco de quebrar teu sistema. Aviso dado, para finalizar nossas variáveis devem conter o seguinte:
CFLAGS="-march=core2 -O2 -pipe -fomit-frame-pointer"
CXXFLAGS="${CFLAGS}"
Alguns ainda utilizam a seguinte forma (o ganho de compilação é imperceptível aos nossos olhos):
CFLAGS="-march=core2 -O2 -pipe -fomit-frame-pointer"
CXXFLAGS="-march=core2 -O2 -pipe -fomit-frame-pointer"
E se você ainda não se cansou de ler esta página gigante, aqui vai mais um aviso: evite espaços desnecessários entre as flags das variáveis, ou isto poderá causar erros de compilação (segredos da cripta do GCC).