Equívocos comuns sobre compiladores
(sbaziotis.com)Equívocos sobre otimização de compiladores
- A otimização fornece o programa ótimo?
- O objetivo do compilador não é gerar o programa ótimo, mas melhorar um programa simplificado.
- A otimização de tamanho de código pode ser viável, mas a otimização de tempo de execução é difícil devido à dificuldade de medição, à ausência de subestrutura ótima e à imprecisão dos modelos de hardware.
- Ao contrário do tamanho do código, o tempo de execução é difícil de medir com precisão, é afetado por vários fatores e não possui subestrutura ótima. Por exemplo, mesmo que dois loops sejam otimizados individualmente, para otimizar o programa inteiro talvez seja necessário fundi-los. Além disso, a otimização é difícil porque não existe um modelo exato do hardware de destino. Por exemplo, o goSLP gera código de vetorização SLP globalmente otimizado, mas, como o modelo de hardware é impreciso, o programa gerado pode não ser ótimo e até ser mais lento que o LLVM.
Equívocos sobre predição de desvio
- Os pesos de desvio são usados pelo preditor de desvios da CPU?
- Na arquitetura x86, o compilador não gera dicas de desvio.
- Os pesos de desvio são usados pelo compilador para o posicionamento de blocos de código. (Ex.: se a probabilidade de desvio for alta, o bloco de destino é colocado logo abaixo do bloco atual para aumentar a localidade no cache de instruções.)
- Na arquitetura Intel Redwood Cove mais recente, as dicas de desvio voltaram a ter alguma relevância, mas ainda é raro que compiladores realmente gerem essas dicas.
Equívocos sobre níveis de otimização
- -O3 gera código muito mais rápido que -O2?
- No caso do Clang, a diferença de desempenho entre -O2 e -O3 não é grande; no caso do GCC, há uma pequena diferença porque o -O2 é menos agressivo que o do Clang.
- O -O3 quase não considera o tamanho do código, então podem surgir problemas de cache de instruções.
- É melhor verificar com benchmarking.
Equívocos sobre interpretadores Javascript e compiladores JIT
- Interpretadores Javascript fazem compilação JIT em tempo de execução porque não é possível saber antecipadamente quais caminhos são quentes?
- Saber quais são os caminhos quentes, por si só, não é suficiente; informações de tipo também são necessárias.
- Como as informações de tipo só podem ser conhecidas em tempo de execução, compiladores JIT compilam o código nesse momento.
Equívocos sobre a relação entre compiladores e interpretadores
- Se existe um compilador, um interpretador não é necessário?
- No caso de C/C++, interpretadores não são muito úteis, mas em casos como WebAssembly eles podem oferecer vantagens como facilidade de desenvolvimento e uso, depuração e segurança.
Equívocos sobre o estágio intermediário do compilador
- O middle-end é independente do alvo/plataforma?
- No caso do LLVM, o estágio intermediário não é totalmente independente do alvo/plataforma.
Equívocos sobre otimização de localidade de dados
- O compilador otimiza a localidade de dados?
- Compiladores otimizam a localidade no cache de instruções, mas quase não otimizam a localidade de dados.
- Otimizar localidade de dados exige grandes mudanças no código, e compiladores de C/C++ não conseguem fazer essas mudanças.
- Para melhorar a localidade de dados, é preciso usar técnicas como design orientado a dados.
Equívocos sobre velocidade de compilação
- -O0 oferece compilação rápida?
- O -O0 gera código depurável e previsível, mas nem sempre garante compilação rápida.
- Em geral, -O0 é mais rápido que -O2, mas isso pode variar conforme o tamanho do projeto e o compilador.
- Para compilação rápida, é possível considerar contornar o pipeline padrão de compilação (ex.: TinyCC) ou gerar LLVM IR diretamente.
Equívocos sobre velocidade de compilação de templates
- Templates são lentos para compilar?
- O motivo de templates em C++ compilarem lentamente está no modelo de compilação do C++.
- Templates em si não degradam drasticamente a velocidade de compilação.
- A biblioteca padrão Phobos de Dlang usa muitos templates, mas compila rapidamente.
Equívocos sobre o valor da compilação separada
- A compilação separada sempre vale a pena?
- A compilação separada pode levar a tempos de link longos.
- Em muitos projetos, builds unity (incluir todo o código em um único arquivo) oferecem melhor desempenho.
- Builds unity trazem vantagens como otimização do programa inteiro, maior velocidade de compilação e melhores logs de erro.
- É raro que a compilação separada seja melhor do que um build unity.
Equívocos sobre otimização em tempo de link (LTO)
- Por que a otimização em tempo de link (LTO) acontece no momento do link?
- A LTO é realizada para otimização do programa inteiro.
- Em teoria, seria mais sensato fazer a otimização do programa inteiro no estágio intermediário, mas, por problemas práticos dos sistemas de build de C/C++ (dificuldade de encontrar arquivos-fonte e identificar relações de chamada), ela é feita no tempo de link.
- Como o linker consegue encontrar todos os object files, o compilador inclui representações intermediárias como LLVM IR nesses arquivos para que o linker possa acessá-las.
Equívocos sobre otimização por inlining
- Inlining é útil principalmente porque remove instruções de chamada de função?
- Remover instruções de chamada de função é uma vantagem, mas o maior benefício do inlining é permitir outras otimizações.
- O inlining possibilita otimizações entre funções.
- Quando o código de várias funções é combinado em uma só por meio do inlining, técnicas de otimização antes aplicadas apenas dentro de uma função podem então ser usadas.
Equívocos sobre o papel da palavra-chave inline
- A palavra-chave inline tem relação com a otimização por inlining?
- Em C++, a palavra-chave inline originalmente era usada como dica para o otimizador, mas desde o C++98 passou a significar "múltiplas definições permitidas".
- No caso do LLVM, a presença da palavra-chave inline adiciona o atributo inlinehint e aumenta o limiar de inlining, mas o efeito não é grande.
- Para sempre fazer inline de uma função, é preciso usar o especificador always_inline.
Equívocos sobre materiais de estudo para compiladores
- LLVM é o melhor compilador para aprender?
- O LLVM também tem valor educacional, mas, por suportar muitos casos de uso diferentes, é complexo e enorme.
- Para aprender desenvolvimento de compiladores, é melhor observar primeiro compiladores menores e mais simples, como o compilador Go, LDC e DMD.
Equívocos sobre comportamento indefinido (Undefined Behavior)
-
Comportamento indefinido só possibilita otimizações?
- Comportamento indefinido também pode desativar otimizações.
-
O compilador pode simplesmente definir o comportamento indefinido?
- O compilador pode definir comportamento indefinido, mas isso pode afetar o desempenho.
- Seria ideal que o compilador definisse todo comportamento indefinido de acordo com o comportamento da plataforma, mas isso não é algo fácil de fazer na prática.
Equívocos sobre geração de código baseada em IA
- Geração de código com 99% de precisão é aceitável?
- Em compiladores, código gerado com 99% de precisão é difícil de usar na prática.
- Um erro em 1% do código causa grandes dificuldades para depuração e manutenção.
- Em projetos de grande escala, esse 1% de erro pode causar problemas muito sérios.
- Como os LLMs atuais são muito lentos em comparação com compiladores, eles não são adequados para geração de código online.
Ainda não há comentários.