Compilador JIT para C, C ++ e afins

33

Existe algum compilador just-in-time disponível para linguagens compiladas, como C e C ++? (Os primeiros nomes que vêm à mente são Clang e LLVM! Mas acho que eles atualmente não o apoiam.)

Explicação:

Acho que o software pode se beneficiar do feedback de criação de perfil em tempo de execução e da recompilação agressivamente otimizada de pontos de acesso em tempo de execução, mesmo para linguagens compiladas na máquina como C e C ++.

A otimização guiada por perfil faz um trabalho semelhante, mas com a diferença, um JIT seria mais flexível em ambientes diferentes. No PGO, você executa o seu binário antes de liberá-lo. Depois de liberado, ele não usaria feedbacks de ambiente / entrada coletados no tempo de execução. Portanto, se o padrão de entrada for alterado, ele será analisado quanto à penalidade de desempenho. Mas o JIT funciona bem mesmo nessas condições.

No entanto, acho que é controverso se o benefício de desempenho de compilação do JIT supera sua própria sobrecarga.

Ebrahim Mohammadi
fonte
1
Recurso fora do tópico.
DeadMG
1
Não tenho certeza se ele se encaixa na pergunta, mas, para uma perspectiva de usabilidade, acho útil o pacote Cxx na linguagem Julia. Ele fornece um prompt C ++ interativo semelhante aos descritos na resposta @ PhilippClaßen.
Antonello
O GCC 9 agora possui um compilador jit gcc.gnu.org/onlinedocs/jit/intro/index.html
user3071643 25/11

Respostas:

33

[Veja o histórico de edições para obter uma resposta bastante diferente, agora basicamente obsoleta.]

Sim, existem alguns compiladores JIT para C e / ou C ++.

CLing (como você pode imaginar no jogo) é baseado em Clang / LLVM. Ele age como um intérprete. Ou seja, você fornece um código-fonte, um comando para que ele seja executado e ele é executado. A ênfase aqui é principalmente na conveniência e compilação rápida, e não na otimização máxima. Como tal, embora tecnicamente seja uma resposta para a pergunta em si, isso realmente não se adequa muito bem à intenção do OP.

Outra possibilidade é NativeJIT . Isso se encaixa na questão de maneira um pouco diferente. Em particular, ele não aceita código fonte C ou C ++, compila e executa. Em vez disso, é um pequeno compilador que você pode compilar no seu programa C ++. Ele aceita uma expressão que é basicamente expressa como um EDSL dentro do seu programa C ++ e gera código de máquina real a partir disso, que você pode executar. Isso se encaixa muito melhor com uma estrutura em que você pode compilar a maior parte do seu programa com um compilador normal, mas possui algumas expressões que você não conhecerá até o tempo de execução, que deseja executar com algo que se aproxime da velocidade ideal de execução.

Quanto à intenção aparente da pergunta original, acho que o ponto básico da minha resposta original ainda permanece: enquanto um compilador JIT pode se adaptar a coisas como dados que variam de uma execução para a seguinte, ou mesmo que variam dinamicamente durante uma única execução, a realidade é que isso faz relativamente pouca diferença, pelo menos como regra geral. Na maioria dos casos, executar um compilador em tempo de execução significa que você precisa renunciar bastante à otimização; portanto, o melhor que você normalmente espera é que seja quase tão rápido quanto um compilador convencional produziria.

Embora seja possível a situações postulado as informações obtidas de um compilador JIT poderia lhe permitem gerar substancialmente melhor código do que um compilador convencional, exemplos de que isso aconteça na prática parecem ser bastante incomum (e na maioria dos casos em que eu fui capaz de verificar está acontecendo, foi realmente devido a um problema no código fonte, não no modelo de compilação estática).

Jerry Coffin
fonte
1
Por que os JITs não salvam um arquivo semelhante ao cache para que possam pular a reaprendizagem de tudo do zero?
JohnMudd
3
@ JohnMudd: Eu suspeito que o raciocínio é segurança. Por exemplo, modifique o código em cache e, na próxima vez em que a VM for iniciada, ele executará o código que eu coloquei lá, em vez do que ele escreveu lá.
Jerry Coffin
4
OTOH, se você pode modificar caches, também pode modificar arquivos de origem.
user3125367
1
@ user3125367: Sim, mas em muitos casos o compilador faz várias verificações de tipo e pode ser ignorado se você carregar o código compilado diretamente do cache. Depende do JIT, é claro - o Java faz muito trabalho de imposição ao carregar um arquivo .class (compilado), mas muitos outros fazem muito menos (quase nenhum, em muitos casos).
perfil completo de Jerry Coffin
11

Sim, existem compiladores JIT para C ++. De uma perspectiva pura de desempenho, acho que a Otimização Guiada por Perfil (PGO) ainda é superior.

No entanto, isso não significa que a compilação JIT ainda não seja usada na prática. Por exemplo, a Apple usa o LLVM como um JIT para seu pipeline OpenGL. Esse é um domínio no qual você tem significativamente mais informações em tempo de execução, que podem ser usadas para remover muito código morto.

Outra aplicação interessante do JIT é o Cling, um intérprete interativo de C ++ baseado em LLVM e Clang: https://root.cern.ch/cling

Aqui está uma sessão de amostra:

[cling]$ #include <iostream>
[cling]$ std::cout << "Hallo, world!" << std::endl;
Hallo, world!
[cling]$ 3 + 5
(int const) 8
[cling]$ int x = 3; x++
(int) 3
(int const) 3
[cling]$ x
(int) 4

Não é um projeto de brinquedo, mas é realmente usado no CERN, por exemplo, para desenvolver o código para o Large Hadron Collider.

Philipp Claßen
fonte
7

C ++ / CLI é ativado. Concedido, C ++ / CLI é não C ++, mas é bem próximo. Dito isso, o JIT da Microsoft não faz os tipos super inteligentes / fofos de otimizações baseadas em comportamento em tempo de execução que você está perguntando, pelo menos não que eu saiba. Então isso realmente não ajuda.

http://nestedvm.ibex.org/ transforma o MIPS em bytecode Java, que seria então acionado. O problema com essa abordagem da sua pergunta é que você descarta muitas informações úteis no momento em que elas chegam ao JIT.

Logan Capaldo
fonte
2

Em primeiro lugar, suponho que você queira um jit de rastreamento em vez de um método.

A melhor abordagem a ser adotada seria compilar o código para llvm IR e adicionar código de rastreamento antes de produzir um executável nativo. Uma vez que um bloco de código se torne suficientemente bem usado e uma vez informações suficientes sobre os valores (e não os tipos como em linguagens dinâmicas) de variáveis ​​foram coletadas, o código pode ser recompilado (a partir do IR) com proteções baseadas nos valores das variáveis.

Parece que me lembro que houve algum progresso em fazer o ac / c ++ jit no clang sob o nome libclang.

dan_waterworth
fonte
1
AFAIK, libclang é a maior parte da funcionalidade de clang fatorada como uma biblioteca. Assim, você pode usá-lo para analisar o código-fonte para criar sofisticada coloração de sintaxe, fiapos, navegação de código, etc.
Javier
@Javier, isso parece certo. Eu acho que havia uma função na biblioteca que pegou um const char * do código-fonte e produziu llvm ir, mas pensando agora, provavelmente é melhor começar com base no ir do que na fonte.
dan_waterworth