Centro de Estudos, Desenvolvimento e Inovação em Software | Universidade de Brasília

Otimização de código

FGA0003 - Compiladores 1 | Semana 9 | Aula teórica

Prof. Sergio Antônio Andrade de Freitas
Compiladores 1 - Engenharia de Software · 2025/2
símbolo CEDIS logo UnB
símbolo CEDISCEDIS
2/27

Objetivos de aprendizagem

  • Definir otimização de código e seus objetivos.
  • Relacionar otimização com o pipeline do compilador.
  • Comparar otimizações manuais, automáticas, independentes e dependentes de máquina.
  • Aplicar a regra as-if em exemplos simples.
  • Identificar técnicas como folding, DCE, strength reduction e CSE.
símbolo CEDISCEDIS
3/27

Roteiro da aula

  1. 01Revisão do pipeline e da RI
  2. 02Conceito de otimização e regra as-if
  3. 03Níveis e tipos de otimização
  4. 04Técnicas clássicas sobre código intermediário
  5. 05Laços, caching, memoization e flags
  6. 06Síntese para o projeto da disciplina
símbolo CEDISCEDIS
4/27

Depois do TAC, por que otimizar?

O código intermediário torna o programa mais fácil de transformar, comparar e testar.

  • TAC explicita dependências e resultados temporários.
  • A AST anotada preserva informações semânticas úteis.
  • A otimização atua sobre formas mais regulares que o código-fonte.
  • O objetivo não é mudar o programa, mas reduzir custo.
Pipeline do compilador com pontos de otimização
A RI é o ponto típico para otimizações independentes de máquina.
seção

Otimizar não é alterar o programa

A transformação só é válida quando preserva o comportamento observável.

símbolo CEDISCEDIS
6/27

O que a otimização tenta melhorar

Tempo

Reduzir operações, desvios e acessos custosos.

💾

Memória

Diminuir uso de temporários, pilha, heap e caches.

📦

Tamanho

Gerar código menor quando espaço é restrição.

Energia

Evitar trabalho desnecessário, especialmente em dispositivos restritos.

símbolo CEDISCEDIS
7/27
atividade

Verificação rápida

Uma otimização de compilador é válida quando:

  1. preserva o comportamento observável do programa.
  2. reduz linhas de código, mesmo alterando a saída.
  3. remove todas as instruções consideradas lentas.
  4. substitui qualquer expressão por uma constante.
QR Code para acesso à atividade
símbolo CEDISCEDIS
8/27
Pipeline mostrando otimização na fonte, RI e alvo
O local da otimização influencia quais informações estão disponíveis.

Onde a otimização pode ocorrer

Otimizações podem atuar em diferentes representações do programa.

  • No código-fonte ou AST: transformações de alto nível.
  • Na RI: folding, propagação, DCE e CSE.
  • No código final: peephole, registradores e instruções específicas.
  • Em JIT: durante a execução, com perfil real.
símbolo CEDISCEDIS
9/27
atividade

Fases de otimização

Em que fases a otimização pode ocorrer em um compilador?

  1. No código intermediário, no código-alvo e até no código-fonte.
  2. Apenas na análise léxica.
  3. Apenas na geração de código final.
  4. Apenas depois que o executável já está pronto.
QR Code para acesso à atividade
símbolo CEDISCEDIS
10/27

Classificações úteis

Quem otimiza?

  • Manual: depende do programador.
  • Automática: feita pelo compilador.
  • Guiada por perfil: usa medições reais.

De que depende?

  • Independente de máquina: atua na RI.
  • Dependente de máquina: explora a arquitetura.
  • Alto nível ou baixo nível, conforme a representação.
símbolo CEDISCEDIS
11/27
atividade

Dependência da arquitetura

Qual exemplo descreve uma otimização de baixo nível e dependente de máquina?

  1. Escolher uma instrução específica para um processador particular.
  2. Trocar uma lista por uma tabela hash no código-fonte.
  3. Renomear variáveis para melhorar legibilidade.
  4. Remover comentários do programa-fonte.
QR Code para acesso à atividade
símbolo CEDISCEDIS
12/27

Regra as-if

O compilador pode transformar o programa como se nada tivesse mudado para o observador externo.

  • Saídas devem permanecer equivalentes.
  • Efeitos colaterais observáveis não podem ser removidos indevidamente.
  • A validade depende da semântica da linguagem.
  • Otimizações agressivas exigem mais cuidado.
Comparação antes e depois da regra as-if
Código sem efeito observável pode ser eliminado.
símbolo CEDISCEDIS
13/27
atividade

Aplicando a regra as-if

O compilador pode eliminar uma instrução quando:

  1. ela não afeta o comportamento observável do programa.
  2. ela aparece dentro de qualquer laço.
  3. ela usa multiplicação, pois multiplicar é sempre caro.
  4. ela foi escrita manualmente pelo programador.
QR Code para acesso à atividade
símbolo CEDISCEDIS
14/27
Mapa de técnicas de otimização
Cada técnica procura um padrão específico de redundância ou custo.

Técnicas clássicas sobre a RI

Muitas otimizações introdutórias podem ser entendidas como padrões locais em TAC ou SSA.

  • Constant folding: avalia constantes em tempo de compilação.
  • Constant propagation: substitui usos por valores conhecidos.
  • DCE: remove código que não contribui para o resultado.
  • CSE: evita recomputar expressões idênticas.
símbolo CEDISCEDIS
15/27

Constant folding e propagation

Antes

  • x = 2 + 3 * 4
  • a = 10
  • y = a + 2

Depois

  • x = 14
  • a = 10
  • y = 12
símbolo CEDISCEDIS
16/27

Código morto e código inalcançável

Dead code

  • A instrução executa, mas seu resultado não é usado.
  • Exemplo: atribuição sobrescrita antes de qualquer leitura.
  • Depende de análise de uso e definição.

Unreachable code

  • O trecho nunca pode ser executado.
  • Exemplo: bloco após return incondicional.
  • Depende do fluxo de controle.
símbolo CEDISCEDIS
17/27

Três padrões frequentes

Strength reduction

Trocar operação custosa por equivalente mais barata, como multiplicação por soma incremental.

Copy propagation

Substituir o uso de uma cópia pelo valor original, quando seguro.

CSE

Calcular uma expressão repetida uma vez e reutilizar o resultado.

símbolo CEDISCEDIS
18/27
atividade

Reconhecimento de técnica

Em x = a + b; y = a + b;, qual técnica evita recomputar a mesma expressão?

  1. Eliminação de subexpressões comuns.
  2. Loop unrolling.
  3. Análise léxica.
  4. Alocação de registradores.
QR Code para acesso à atividade
símbolo CEDISCEDIS
19/27

Otimizações de laço

Laços concentram custo porque repetem operações muitas vezes.

  • Loop-invariant code motion move cálculos estáveis para fora do laço.
  • Loop unrolling reduz overhead de controle e expõe paralelismo.
  • Fusion melhora localidade quando laços percorrem os mesmos dados.
  • Fission pode facilitar paralelização ou reduzir pressão por registradores.
Exemplos de otimizações de laço
O ganho depende das dependências e da arquitetura.
símbolo CEDISCEDIS
20/27

Caching e memoization

Caching

  • Reutiliza dados já obtidos ou computados.
  • Pode ocorrer em software ou hardware.
  • Nem sempre depende de funções puras.

Memoization

  • Armazena resultados de chamadas de função.
  • Funciona melhor com funções puras.
  • Evita recomputação de entradas repetidas.
símbolo CEDISCEDIS
21/27
Comparação dos níveis de otimização
Níveis mais altos não significam melhor resultado em todos os programas.

Flags de otimização

Compiladores reais oferecem níveis que equilibram desempenho, tamanho e depuração.

  • -O0: favorece depuração e previsibilidade.
  • -O1 e -O2: aplicam otimizações comuns com custo moderado.
  • -O3: pode aumentar código e tempo de compilação.
  • -Ofast: pode relaxar garantias numéricas e padrões.
símbolo CEDISCEDIS
22/27
A otimização prematura é a raiz de muitos problemas de engenharia.
Síntese inspirada na advertência clássica de Donald Knuth
símbolo CEDISCEDIS
23/27

Como aplicar no projeto da disciplina

Escolha uma RI

Use TAC ou estrutura equivalente que possa ser impressa e testada.

Defina um padrão

Exemplo: t1 = 2 + 3 vira t1 = 5.

Implemente um passe

Percorra a lista de instruções e gere uma versão otimizada.

Teste antes e depois

Mostre equivalência e redução de custo quando possível.

símbolo CEDISCEDIS
24/27

Roteiro mínimo de uma otimização didática

1

Detectar

Reconhecer o padrão na RI ou no TAC.

2

Verificar

Confirmar que a transformação preserva a semântica.

3

Transformar

Produzir instruções equivalentes e mais simples.

4

Validar

Comparar saídas e registrar casos de teste.

símbolo CEDISCEDIS
25/27

Síntese

  • Otimizar é reduzir custo preservando comportamento observável.
  • A RI facilita transformações independentes de máquina.
  • Técnicas clássicas removem redundância, código inútil e recomputação.
  • Laços exigem atenção por concentrar custo e dependências.
  • No projeto, priorize corretude, testes e uma otimização pequena bem demonstrada.
símbolo CEDISCEDIS
26/27

Referências

  1. Aho, A. V., Lam, M. S., Sethi, R., & Ullman, J. D. (2007). Compilers: Principles, Techniques, and Tools (2nd ed.). Addison-Wesley.
  2. Appel, A. W. (2002). Modern Compiler Implementation in Java (2nd ed.). Cambridge University Press.
  3. Cooper, K. D., & Torczon, L. (2012). Engineering a Compiler (2nd ed.). Morgan Kaufmann.
  4. Tremblay, J. P., & Sorenson, P. G. (2008). Theory and Practice of Compiler Writing. BS Publications.
  5. Wirth, N. (2005). Compiler Construction. ETH Zurich.
símbolo CEDISCEDIS
27/27

Encerramento

Na próxima prática: aplicar otimizações simples e testar o impacto sobre o código intermediário.

→ avançar · ← voltar · deslize (touch) · n notas · f tela cheia · p imprimir