Trace de JIT no mundo real no Core Dev Sprint do CPython
(antocuni.eu)Um resumo aprofundado dos desafios técnicos enfrentados pelo novo compilador JIT (Just-In-Time) baseado em tracing do CPython.
Bloqueadores de trace (Trace Blockers)
O JIT de tracing identifica caminhos de código executados com frequência ("caminhos quentes") durante a execução do programa e gera código de máquina otimizado registrando as micro-operações desses caminhos. No entanto, esse processo é interrompido quando o JIT encontra código cujo interior ele não consegue inspecionar, ou seja, "bloqueadores de trace". No caso do CPython, um exemplo representativo é a chamada de funções de extensão escritas em C.
- Explicação técnica: o blog usa como exemplo uma função em Python puro que calcula pi (π). O JIT do PyPy otimiza esse loop de cálculo numérico de forma muito eficiente e apresenta desempenho 42 vezes superior ao do CPython. Porém, se for adicionada ao loop apenas uma única chamada de função em C que o JIT não consegue rastrear (
hic_sunt_leones()), o desempenho do PyPy cai drasticamente para apenas 1,8 vez o do CPython. Esse único "bloqueador de trace" praticamente neutraliza a maior parte da capacidade de otimização do JIT. Isso acontece porque o JIT não conhece o funcionamento interno da função em C e, por isso, não consegue otimizar o loop inteiro como uma única unidade, passando a tratar separadamente o código antes e depois da chamada da função em C.
Fluxo de controle orientado por dados (Data-Driven Control Flow)
Esse problema ocorre quando o fluxo de controle do programa varia muito de acordo com os dados de entrada. O JIT de tracing funciona melhor sob a premissa de que existe um "caminho quente" consistente, mas essa premissa deixa de valer quando o caminho de execução continua mudando conforme os dados.
- Explicação técnica: o blog usa como exemplo uma função que recebe 9 argumentos. Cada argumento pode ser
Noneou um número, e dentro da função aparecem em sequência condicionais no formatoif <var> is None: .... Nesse caso, o caminho de código executado muda a cada vez conforme a combinação de valoresNonepassados nos argumentos. Assim, o JIT se depara com o problema de um "número exponencial de traces". Em outras palavras, o JIT tenta gerar código otimizado separado para todas as combinações possíveis de argumentosNone, o que provoca um enorme overhead e acaba levando a um resultado muito mais lento do que o do CPython sem JIT.
Em conclusão, esta postagem de blog destaca que, para que o novo JIT de tracing do CPython se estabeleça com sucesso, será preciso resolver os problemas de "bloqueadores de trace" e "fluxo de controle orientado por dados". Isso sugere que não se trata apenas de uma questão de implementação, mas possivelmente de uma limitação fundamental da própria tecnologia de JIT de tracing.
Ainda não há comentários.