- Tail Call: chamada de função feita imediatamente antes de a função retornar. Quando ocorre a otimização de Tail Call, a instrução
jmp é usada para reduzir a pilha de chamadas.
- Vantagens:
- Reduz o uso de memória da pilha de O(n) para O(1).
- Remove o overhead de desempenho das chamadas de função, permitindo uso como uma estrutura eficiente de controle de repetição.
Problemas do loop de interpretador
- Problemas:
- Quanto maior a função e mais complexo o fluxo de controle, mais difícil é manter dados importantes nos registradores.
- Quando caminhos rápidos e lentos se misturam, a qualidade do código piora.
Melhorando o loop de interpretador com Tail Call
- Solução: usar Tail Call para separar cada tarefa em pequenas funções, com cada função chamando a próxima tarefa por meio de Tail Call.
- Vantagens:
- Permite controlar a alocação de registradores.
- Separa caminhos rápidos e lentos para manter a qualidade do código.
- Permite otimizar sequências de instruções independentes.
Limitações
- Problema na presença de non-tail calls: se houver chamadas que não sejam Tail Call, frames de pilha são criados e os dados são armazenados na pilha, causando perda de desempenho.
- Tratamento complexo de exceções: quando o tratamento de exceções é complexo, aumentam a duplicação de código e a complexidade.
- Problema de portabilidade: como o atributo
musttail não é padrão, ele não é suportado por todos os compiladores.
Resumo do GN⁺
- A otimização de Tail Call desempenha um papel importante no ganho de desempenho e mostra resultados especialmente fortes no parsing de Protobuf.
- Essa técnica também pode ser aplicada a importantes interpretadores de linguagens escritos em C, como Python, Ruby, PHP e Lua.
- O problema de portabilidade do atributo
musttail continua sendo um desafio a resolver.
- Projetos com funcionalidade semelhante incluem LuaJIT e o interpretador WebAssembly wasm3.
1 comentários
Comentários no Hacker News
A proposta do padrão C inclui chamadas de cauda na forma
return goto (expression);[[musttail]], isso garante a vida útil dos objetos locais e não exige uma análise de escape amplaPara os entusiastas de Rust, havia um RFC antigo para adicionar a palavra-chave
becomeEm C++, a forma como interpretadores geralmente ganham velocidade é usando goto computado
O problema de trocar contexto usando chamadas de cauda exige funções que usem a convenção de chamada
Há esperança de que o atributo
[[musttail]]se espalhe para GCC, Visual C++ e outros compiladores populares[[musttail]]está em processo de ser adicionado ao GCCAo mencionar suporte a C++, aponta-se que em C++ quase não há chamadas de cauda
Há curiosidade sobre o que acontece se uma exceção for lançada em uma função C++
[[musttail]]Menciona-se que um exemplo simples não precisa de
__attribute__((musttail))para gerar bom códigoHá curiosidade sobre a velocidade de usar trampolins, retornando um ponteiro de função para ser chamado em um loop externo
Há um pedido para esclarecer um exemplo de caminho de exceção encapsulado com
[[musttail]][[musttail]]impede a criação de stack frames e o spill de registradores