Godot: Melhoria do desempenho do GDScript através da compilação Just-in-time (JIT)

Criado em 5 jun. 2016  ·  105Comentários  ·  Fonte: godotengine/godot

Que tal adotar a compilação just-in-time (JIT) para GDscripts?

A compilação JIT não tem desvantagens em relação ao código interpretado (além de uma inicialização mais lenta, em alguns casos) e muitas vantagens, principalmente em termos de desempenho.

Para a compilação JIT, existem pelo menos duas maneiras populares de implementá-lo de maneira multiplataforma sem escrever nenhum código do zero:

Há também a opção de usar PyPy, JVM ou CLR, mas esses são bem mais pesados, usando GNU Lightning ou libjit (não tenho certeza se a licença é compatível com a de Godot) e claro, escrevendo um gerador de código do zero (o que poderia levar anos).

Portanto, a escolha é praticamente entre LLVM IR e DynASM . Ambos têm um desempenho muito bom, mas o DynASM é escrito em Lua e o LLVM tem uma pegada bastante grande (~20 MB), mas também oferece outros recursos.

É claro que compilar GDScript diretamente para C++ também pode ser uma alternativa válida - o único mecanismo de jogo que eu conheço (se entendi corretamente) é o Enigma , que compila sua própria linguagem de script (EDL) para C++.

O que você pensa sobre isso?

archived discussion feature proposal gdscript

Comentários muito úteis

Estou trabalhando na compilação JIT para minha tese de mestrado e estou planejando implementar algo adequado para Godot. Até agora, li muitos artigos sobre o assunto, especificamente sobre como lidar com a natureza dinamicamente tipada do GDScript. Agora comecei a trabalhar em um protótipo e espero ter algo para mostrar em breve.

Todos 105 comentários

A compilação JIT não tem desvantagens sobre o código interpretado

Há um: portabilidade…

Mas a implementação da tipagem estática não seria melhor e mais fácil, pelo menos por enquanto? Sem mencionar o suporte C# que pode eventualmente acontecer?

@Calinou A portabilidade não é uma grande preocupação, pois poderíamos apenas executar o GDScript como fazemos agora em plataformas não suportadas.

a digitação estática também pode ser jitted opcionalmente

A portabilidade do

O LLVM IR na verdade facilitaria a adoção da tipagem estática (no back-end, é claro que o GDScript ainda precisaria de inferência de tipo ou assinaturas de tipo explícitas).

Se o suporte a C# for implementado vinculando-se ao Mono ou ao CoreCLR, ambos incluem JITs, portanto, esse problema será resolvido automaticamente. Eu posso entender porque C# (é uma linguagem melhor que Java e Unity já a usa), mas para esses propósitos, a JVM provavelmente traria melhor desempenho que Mono ou CoreCLR (especialmente por causa do coletor de lixo JVM, que é adequado para jogos já que não congela nada). Após o Java 8, eu pessoalmente não acho que a maioria das pessoas se oporia a ele em vez de C# (já que as vantagens trazidas pelo C# neste momento se resumem principalmente a Generics e LINQ, ambos não tão úteis no desenvolvimento de jogos, o que é principalmente imperativo) , e usar a JVM dá acesso a muitas linguagens funcionais mais organizadas do que C#, como Clojure, Scala e Kotlin.

Além disso, o LLVM torna possível usar C++ de maneira JITted. O Unreal Engine também usa C++ para scripts, então não acho que seria muito estranho (desde que as falhas de segmentação sejam mais comuns, mas o GDScript ainda pode ser oferecido a programadores menos experientes). C++14 (suportado pelo LLVM em sua maior parte) é uma linguagem bastante pequena, mesmo em comparação com Java e C#, se usada corretamente (é claro, os tempos de compilação são longos e as mensagens de erro ainda não são tão boas).

Eu daria suporte à compilação JIT para GDscript, desde que isso não signifique o fim da linguagem (ou seu respectivo editor embutido) como é agora,

Minha proposta.

  • Adicione um novo tipo de variável estática , que por sua vez pode ser criada fazendo algo como
    static var float(5.5) ou algo assim (a diferença principal seria maior desempenho e melhor análise devido à expectativa de ser um tipo específico. Este método deve permitir o uso de tipagem estática mantendo as vantagens da tipagem dinâmica e eliminando qualquer necessidade para reescrever scripts se você quiser usá-los
  • Depois disso, adicione uma rotina de compilação JIT que converte GDscript para C++ nos bastidores (ou seja, invisível para o usuário), mas visível na forma de desempenho quando o jogo é executado. A idéia de usar C++ aqui significaria que os desenvolvedores Godot não teriam que adicionar um monte de novas bibliotecas ao código-fonte.

JIT em linguagens de tipagem dinâmica como Lua é feito fazendo inferência de tipos
do ponto de entrada através da árvore de chamada, indo o mais fundo possível. Eu estou
não dizendo que isso não seria possível em Godot, já que praticamente todas as entradas
ponto é digitado. A conclusão de código em Godot faz algo semelhante, o que
é por isso que é capaz de adivinhar tantas informações de tipo.

No entanto, existem muitos casos em que o tipo não é óbvio e, embora o
A conclusão de código funciona muito bem ao adivinhar as chamadas get_node() na árvore
sendo editado, esta informação pode ser diferente ou mudar em tempo de execução.
Sinceramente, não tenho certeza de quão eficiente a inferência de tipos pode ser.

Para ajudar nisso, seria fácil permitir que o GDScript permitisse que o usuário
"forçar" o tipo de uma variável (algo que não é possível em lua). Mas se você
vão fazer isso, então você pode simplesmente digitar estática completa.

Não estou convencido de nenhuma abordagem no momento.

Em domingo, 5 de junho de 2016 às 17h37, Ace-Dragon [email protected] escreveu:

Eu apoiaria a compilação JIT para GDscript, desde que isso não signifique que o
final da linguagem (ou seu respectivo editor embutido) como é agora,

Minha proposta.

  • Adicione um novo tipo de variável _static_, que por sua vez pode ser criado por
    maneira de fazer algo como
    static var float(5.5) ou algo assim (a diferença principal seria
    maior desempenho e melhor análise devido à expectativa de ser um
    tipo específico. Este método deve permitir o uso de tipagem estática enquanto
    mantendo as vantagens da tipagem dinâmica e eliminando qualquer necessidade de
    reescrevendo scripts se você quiser usá-los
  • Depois disso, adicione uma rotina de compilação JIT que converte GDscript para
    C++ nos bastidores (ou seja, invisível para o usuário), mas visível no formulário
    de desempenho quando o jogo é executado. A ideia de usar C++ aqui significaria
    que os desenvolvedores Godot não teriam que adicionar um monte de novas bibliotecas para
    a fonte.


Você está recebendo isso porque comentou.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment -223836149,
ou silenciar o thread
https://github.com/notifications/unsubscribe/AF-Z2znHs58L-KDgtIjHYYjgHVUGXgwpks5qIzOngaJpZM4IuWZe
.

Haxe usa inferência de tipo mesmo que seja seguro de tipo sob o capô. Pessoalmente, gosto do fato de não precisar definir o tipo de cada variável, mas você pode fazê-lo se precisar. Dessa forma, você tem o melhor dos dois mundos: você não precisa especificar tipos (desde que o compilador possa percebê-los) e escrever métodos "genéricos"/"modelos" resulta em uma sintaxe muito mais limpa como em linguagens de tipagem estática como C++ ou C#, mas efetivamente o código é seguro para tipos e você pode especificar o tipo solicitado sempre que quiser usá-lo para tornar o código mais legível.

Em Haxe é feito usando a sintaxe
var <name> [: <Type>] [=<initial value>]
o que parece muito estranho vindo de linguagens tipadas, mas faz todo o sentido vindo de uma linguagem como GDScript.

Mas eu não tenho ideia de como isso afeta a complexidade e eficácia da compilação JIT, eu apenas queria apontar que isso seria uma espécie de meio termo entre a tipagem dinâmica como é agora e a tipagem estática completa como é em linguagens como C++.

Eu usei Haxe por 1 ~ 2 anos atrás.
Eu gosto de poder definir o tipo opcionalmente para variável e função de retorno como @Warlaan disse.

Eu prefiro o estilo de declaração de tipo c++ antes do typescript/haxe:

var x = 50
int y = 60
float z =70

Ou talvez possamos espelhar o estilo de export :

type(int) var y = 60
type(float) z =70

@bojidar-bg Pessoalmente, prefiro o estilo c++ (muito menos digitação).

Existem muitos problemas que podem surgir ao colocar o tipo antes da variável. Go foi escrito por alguns dos implementadores C mais experientes de todos os tempos (incluindo Ken Thompson) e eles usaram a notação Postfix (como Pascal e Haxe).

Acho que essa discussão pertence a uma discussão separada. Não estou propondo uma votação porque é claro que a maioria das pessoas está acostumada a linguagens tipadas como C++, C# e Java e poderia votar na sintaxe familiar, mas apenas para reunir materiais de pesquisa e opiniões para encontrar a solução mais adequada para uma linguagem como GDScript (que claramente não é semelhante a C em nenhum outro aspecto).

Concordo em todos os quatro aspectos:

  1. A sintaxe C++ é mais agradável de ler, mas
  2. imho a notação postfix faz mais sentido aqui
  3. que eu preferiria discutir (não decidir por maioria)
  4. em outro fio. Desculpe, eu não queria invadir o tópico quando mencionei o tópico. Voltemos à questão da compilação JIT.

A implementação do JIT via LLVM significaria que o Clang seria preferível ao GCC. É possível compilar o LLVM JIT usando o GCC?

Ao contrário de Java, Python, C# etc, o GD Script funciona como parte de um mecanismo de código nativo C++ maior. Na verdade, tanto o GD Script quanto o resto do motor são uma unidade. Alguns componentes do jogo são C++ e alguns são GD Script. No geral, o desempenho do jogo depende da criação de um equilíbrio entre os dois. Escrever um jogo Godot apenas em script o tornaria mais lento, mas escrevê-lo principalmente através de nós com o mínimo de script gd o tornaria mais rápido.

Então minha pergunta é. Esse pequeno aumento no desempenho realmente vale o esforço?

Eu acho que a adição de C# pode de fato ser melhor para o mecanismo do que implementar JIT no GD Script. C# é provavelmente uma linguagem muito mais otimizada e eficiente, portanto, provavelmente tão rápida quanto GD Script + JIT, mas não tão integrada quanto GD Script.

Para responder à minha própria pergunta, pessoalmente, não vejo isso como uma questão importante no momento. Talvez no futuro, quando Godot tiver todos os recursos desejados e os desenvolvedores, simplesmente otimizem esses recursos.

A implementação do JIT via LLVM significaria que o Clang seria preferível ao GCC. É possível compilar o LLVM JIT usando o GCC?

Sim, mas o LLVM se tornaria uma dependência, então faria mais sentido usar o LLVM para tudo.

Eu não acho que o aumento no desempenho seria pequeno para jogos maiores. Talvez para jogos de arcade não fosse perceptível, mas em jogos com muitos loops e chamadas de função, definitivamente seria.

Além disso, há uma discussão sobre por que C# sobre Java ou mesmo C++? Acho que C# é uma linguagem muito boa, mas o CLR e as ferramentas são definitivamente inferiores à JVM (especialmente em outras plataformas além do Windows).

@paper-pauper você deve fazer um post no fórum sobre Java, JIT para continuar essa discussão. Na verdade, prefiro Java > C# tbh. Acho que C# não foi uma escolha, mas mais uma oportunidade que os desenvolvedores decidiram aproveitar.

para que no final decidimos? vai JIT ou não?

Que tal a capacidade de compilar GDScript para código C++ estático? Isso seria mesmo possível?

@trollworkout : provavelmente seria mais simples e mais otimizado escrever sua lógica de jogo diretamente em C++, não seria?

@SuperUserNameMan Não necessariamente. Primeiro você estaria usando C++ não GDScript e segundo você ainda precisa descobrir uma maneira de carregar um objeto binário como uma dll e conectá-lo ao mecanismo sem ter que recompilar o mecanismo com suas alterações em C++.

@trollworkout Graças ao #3936, pode ser possível compilar GDScript para C e carregá-lo dinamicamente, eventualmente.

não é necessário traduzir o script em c++. Direitos de @SuperUserNameMan

ou precisa de JIT ou c# e todos ficarão felizes

nah, o outro tópico vinculado ao @paper-pauper é realmente muito mais interessante. A idéia é usar o binário C como um tipo de linguagem de script e chamar o código diretamente via reflexão, em vez de ser interpretado, tornando-o tão rápido quanto o código nativo C++, acho que isso resolveria o problema da ABI. Na verdade com isso você não precisa mais de ABI.

Atualmente estou experimentando a implementação de um GDScript JIT usando RPython. Os primeiros resultados (muito preliminares) são promissores, mas não sei quão difícil será integrá-lo ao motor.

Ainda há interesse em um GDScript JITed ou as pessoas são mais interessantes em c# ou GDScript estático?

Considerando que você está realmente tentando uma solução possível, eu digo vá em frente e veja se há ganho de desempenho suficiente nos jogos para valer a pena.

O que quero dizer com isso é que só valeria a pena perseguir se houver um ganho de desempenho bastante grande para lógica complexa (ou, caso contrário, esses ganhos podem ser melhor obtidos apenas fazendo um trabalho de otimização geral na própria linguagem).

Performance é crucial para mim, simplesmente não consigo escolher Godot para o roteiro
Desempenho. Mas acho que uma linguagem mais rápida (Lua, Java, c#) é melhor que o Jit, já que o Jit não funciona no iOS. E compilar para c++ perde a possibilidade de atualizar a lógica do jogo no iOS

Lua não é mais rápida apenas por causa do JIT? Ou tem algum tipo de compilação AOT também?

Ainda prefiro um GDScript tipado estaticamente (talvez um sistema de digitação opcional, como o TypeScript é para JavaScript). Também ter GDScript -> transpilação C++ resolveria muitos problemas de desempenho, mas posso ver que isso está longe de ser trivial.

Como você atualiza a lógica do jogo no ios? você não pode baixar scripts de
seus jogos e executá-los no ios, tudo tem que ser instalado a partir do seu
ipa original publicado na app store. De qualquer forma você tem que
atualize o aplicativo para obter uma nova lógica, caso sejam novos scripts ou um novo tempo de execução
com a lógica compilada.

Em 20 de agosto de 2016 às 05:05, George Marques [email protected] escreveu:

Lua não é mais rápida apenas por causa do JIT? Ou tem algum tipo de AOT
compilação também?

Eu ainda prefiro um GDScript digitado estaticamente (talvez um tipo opcional
sistema, como o TypeScript é para JavaScript). Também tendo GDScript -> C++
transpilação resolveria muitos problemas de desempenho, mas posso ver isso
está longe de ser trivial.


Você está recebendo isto porque está inscrito neste tópico.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment -241175124,
ou silenciar o thread
https://github.com/notifications/unsubscribe-auth/AGVmPZDOcIkO1-6ayDySyPnCfJdRk5Saks5qhm76gaJpZM4IuWZe
.

GDScript estaticamente tipado com VM otimizada também seria bom, existe algum plano para isso?

Sistema de digitação opcional para GDScript, como sugerido por @vnen, seria perfeito :)

Enquanto pesquisava sobre isso, encontrei o ótimo SLJIT. Eu tenho tentado descobrir como eu poderia integrá-lo para permitir a compilação JIT pelo menos no nível da função GDScript.

Depois de algumas horas de leitura/pensamento cheguei à conclusão de que seria de pouco benefício porque o maior gargalo, agora que estudei o código profundamente, é provavelmente todo o material Variant .

Terminar minha tarefa para um teste seria um exagero, então eu o abandonei. De qualquer forma um branch com meu trabalho inicial está no meu repositório caso alguém queira jogar com SLJIT e tê-lo já integrado ao sistema de compilação.

Então, eu me junto ao grupo que acredita que a tipagem estática removeria muitas chamadas e ramificações, melhorando notavelmente o desempenho.

@RandomShaper

Concordo que o JIT não é viável atualmente, mas o problema é Variant e não a tipagem dinâmica.

Eu experimentei escrever GDScript JIT usando RPython, que é a base para PyPy e feito especificamente para criar JITs para linguagens dinâmicas.

Discordo que o problema seja a tipagem estática, seria possível ter um GDScript JIT rápido se não tivéssemos que trabalhar com Variant . Variant replica algumas das funcionalidades das linguagens dinâmicas e, infelizmente, essa é a parte em que o JIT do RPython obtém seu aumento de velocidade. O RPython precisa saber quais tipos ele possui atualmente e pode acelerar essas partes.

Variant está tão profundamente enraizado no Godot, no entanto, que é realmente difícil ficar sem ele. Eu teria que basicamente reimplementar todos os tipos de dados GDScripts e transformar em Variantes depois.

Se alguém tiver uma ideia de como podemos passar sem usar Variantes, um GDScript rápido seria possível (uma prova de conceito GDScript JIT minha que não usava Variant era promissora. Um loop (bobo) que em GDscript levava 20 segundos era feito em menos de um segundo quando JITted)

Bem, eu estava tratando Variant e tipagem dinâmica como se ambas fossem a mesma
coisa ou como se um não pudesse existir sem o outro.

Mas provavelmente estou perdendo algo no design de linguagem.

AIUI Variant torna possível usar C++ em Godot como se fosse uma linguagem tipada dinamicamente. Especialmente no tipo de retorno para alguns métodos. Eu não estou familiarizado com a fonte, mas não tenho certeza se há uma maneira limpa de contornar isso.

@brakhane Quer compartilhar seu experimento RPython? Acho que todo mundo aqui se beneficiaria de dar uma olhada nisso :smile:

Variant está tão profundamente enraizado em Godot, no entanto, que é muito difícil ficar sem ele. Eu teria que basicamente reimplementar todos os tipos de dados GDScripts e transformar em Variantes depois.

Em teoria, qual seria a desvantagem disso (além do tempo necessário para implementá-lo)? Esse tipo de casting não é tão caro, até onde eu entendo.

@paper-pauper veja PyPy e RPython - Python é estritamente tipado dinamicamente, e PyPy pode acelerar o código Python muito bem pensado ... RPython é muito bom para desenvolvimento de intérpretes (com JIT quase livre)

Um artigo realmente interessante foi publicado sobre (atualmente em desenvolvimento) o novo Ruby JIT.

A parte chave aqui é o Ruby escolher implementar seu JIT gerando código C e chamando um compilador C externo para construir um .so então carregá-lo. Escusado será dizer que isso gerou muitos interrogatórios e este artigo começa a dar algumas respostas.

Basicamente, as vantagens de usar um compilador externo (em oposição a integrar um framework JIT) são:

  • independência para um único compilador (pode usar GCC ou LLVM dependendo do que estiver disponível/melhor)
  • Frameworks JIT como libGCCJit são jovens, então sua API pode ser muito mais frágil que C

Claro que isso precisa ser tomado com muitas precauções (os benchmarks do artigo comparam um protótipo com projetos prontos para produção, como JRuby), mas mostra que, desde que você use otimizações inteligentes, como cabeçalhos pré-compilados, usar um compilador externo pode ser um solução viável.

Do ponto de vista de Godot, esta técnica pode ser útil também para fornecer uma compilação estática do GDscript.

Estou trabalhando na compilação JIT para minha tese de mestrado e estou planejando implementar algo adequado para Godot. Até agora, li muitos artigos sobre o assunto, especificamente sobre como lidar com a natureza dinamicamente tipada do GDScript. Agora comecei a trabalhar em um protótipo e espero ter algo para mostrar em breve.

@DoctorAlpaca Estou quase terminando as verificações de tipo estático para GDScript, acredito que isso pode ajudá-lo pelo menos um pouco (você pode procurar meu fork se estiver interessado e também # 10630). Eu estava planejando olhar para o JIT eventualmente, mas não tenho experiência com isso.

Algum tempo atrás eu comecei um experimento sobre JIT-ting GDScript. Eu configurei alguns fundamentos para isso.
Caso você queira dar uma olhada... https://github.com/RandomShaper/godot/tree/exp-gdscript-jit-master

@vnen Na verdade, estou muito animado para ver quanta diferença no desempenho da tipagem estática pode trazer, em oposição ao perfil de tipo que estou planejando fazer. Como também terei que produzir algum texto real para o meu mestrado, acho que não vou fazer muito mais do que um protótipo, no qual haverá muito espaço para melhorias.

@RandomShaper Essa configuração parece legal e, depois de analisar as opções disponíveis, provavelmente também usarei o SLJIT. Obrigado por isso!

Os resultados da pesquisa de

Eu pessoalmente não acho que JIT GDScript seria uma boa ideia:

  • O JIT não é permitido em plataformas móveis (pelo menos no IOS, não tenho certeza quanto ao Android), que são as que mais precisam desse tipo de otimização...
  • JIT é uma grande bagunça. Existem várias abordagens (rastreamento vs método JIT), algumas delas nem mesmo determinísticas :'-(
  • JIT é uma grande bagunça. A depuração é complexa (envolvendo despejos de código jited e memória ...), assim como a criação de perfil, assim como a manutenção do código :'-(
  • JIT é um tipo especial de confusão. Os programadores JIT são um recurso escasso, portanto, manter isso seria complicado. Isso poderia levar a tornar o GDScript evoluir cada vez mais difícil, já que o interpretador e o JIT estariam fortemente acoplados.

O projeto luaJIT ilustra bem esses pontos: o projeto é simplesmente brilhante (base de código muito boa, desempenho muito bom), resolve um caso de uso real, é usado por empresas comerciais.
E, no entanto, está morrendo lentamente, já que seu autor principal recuou, incapaz de acompanhar a nova versão lua (luaJIT parou em 5.1, argumentando que a adição de inteiro em cima de float na linguagem feita pelo 5.2 seria muito complexa para add), mesmo que a comunidade lua tenha um tamanho médio e luaJIT seja o projeto mais conhecido.

Obviamente GDScript é uma ordem de magnitude mais simples que Python ou mesmo lua, mas o ônus de fazer um JIT continua sendo importante, especialmente considerando que não é um recurso central do projeto Godot.

A única solução aceitável aqui seria confiar no llvm, que cuidaria de todas as partes malucas e nos deixaria apenas enviar o código llvm-ir para ele. No entanto, como dito antes, llvm é uma grande dependência (neste ponto quase poderíamos enviar v8 com Godot e escrever um GDScript para transpilador Javascript :trollface: ), portanto, pesquisas adicionais para entender o quanto seria adicionado são necessárias aqui antes de ir qualquer lugar. E, novamente, isso não seria uma solução viável para pelo menos a plataforma IOS (maldita Apple!).

Desculpe soar tão sombrio aqui, mas eu não postaria isso sem uma proposta ;-)
Acho que o que os eleitores entendem por "JIT GDScript" é "GDScript, mas rápido como código nativo". Então, no final, não importa realmente qual é o meio para atingir esse objetivo certo (AOT, JIT, transpiling + compilação etc.) ?

Pelo que vi do GDScript, um recurso muito legal (falta) é que não é uma linguagem dinâmica. Não há palavra-chave exec , nenhum patch de macaco, você não pode modificar a variável durante a depuração, etc.
Isso torna a linguagem adequada para compilação estática (e, de fato, é mais uma razão para não usar JIT). Além disso, o novo sistema de tipagem estática deve permitir uma otimização fácil (compare com a inferência de tipos).

Então, minha ideia seria começar fazendo um transpilador GDScript-to-C simples (sem otimizações, apenas use Variant em todos os lugares), que estaria disponível como godot --gdcc <myfile.gd> . Usar o compilador GDScript existente e a API GDNative deve tornar isso relativamente fácil.
Os usuários são então deixados para lidar com o resto da cadeia de ferramentas (por exemplo, compilar cada arquivo C como um .so e substituir o arquivo .gd original por um arquivo .gdns apontando para o .so).

Em uma segunda vez, poderíamos começar a nos perguntar sobre otimizações e, mais importante, uma maneira de enviar um compilador para o Godot.
Considerando este segundo ponto, o tinycc parece uma solução realmente interessante devido ao seu baixo peso, número de plataformas suportadas (x86, amd64, arm, arm64 para linux, win32 e osx), libtcc.h api que permite integrá-lo em um programa e cruza o modo de compilação. Além disso, a versão 0.9.27 foi lançada em dezembro passado (então o projeto parece ainda vivo ^^).

Finalmente, da mesma forma que atualmente transformamos arquivos .gd em .gdc ou .gde na hora da exportação, poderíamos criar um novo tipo (.gdso?) automaticamente).

Concordo que seria um exagero escrever um compilador JIT para GDScript. Como você mencionou, a compilação JIT não é permitida no iOS (o Android permite), então você teria que voltar para a lentidão interpretada nessa plataforma ou escrever um compilador AOT (olha! agora você precisa manter dois compiladores, bem como o intérprete).

Eu disse muitas vezes que considero o transpilador GDScript2C a melhor opção agora que temos GDNative.

Para binários construídos com o módulo mono, você poderia escrever um compilador GDScript2IL e então teria a compilação JIT e AOT.

Pelo que vi do GDScript, um recurso muito legal (falta) é que não é uma linguagem dinâmica. Não há palavra-chave exec , nenhum patch de macaco, você não pode modificar a variável durante a depuração, etc.
Isso torna a linguagem adequada para compilação estática (e, de fato, é mais uma razão para não usar JIT).

Na verdade, no GDScript você pode modificar o código-fonte de um script em tempo de execução.

Na verdade, no GDScript você pode modificar o código-fonte de um script em tempo de execução.

@neikeq eu procurei por isso antes de escrever meu post, acho que não olhei o suficiente ^^
pode me indicar um exemplo? (ou onde é implementado na base de código)

Na verdade, no GDScript você pode modificar o código-fonte de um script em tempo de execução.

Não tanto quanto "modificar". Você pode definir a propriedade source_code de Script com um código totalmente novo e então chamar reload() para recompilá-lo. Tecnicamente, isso também é implementado para C#, pois faz parte da API de script.

@touilleMan sua ideia é semelhante ao que é discutido em # 11068.

O problema de transpilar GDScript, ou compilá-lo antecipadamente, é que mesmo com o sistema de tipo estático todas as chamadas de método ainda precisam ser virtuais. Qualquer coisa pode se esconder atrás de um parâmetro Node . Portanto, você teria problemas de desempenho semelhantes aos do Java, que usa JIT em seu bytecode pelos (em parte) pelos mesmos motivos.

De qualquer forma, como preciso de uma parte prática para minha dissertação de mestrado, vou implementar um JIT para GDScript de qualquer maneira, para depois vermos o quão bem ele se sai e o quanto ele destrói a base de código. Sinceramente, não me importaria, mesmo que permanecesse um protótipo para sempre e nunca fosse mesclado.

Além disso, para as coisas dinâmicas, você pode carregar arquivos de script de qualquer lugar no disco e usá-los para qualquer coisa que você possa usar scripts em seu projeto. O que é um recurso muito bom para DLC e/ou conteúdo criado pelo usuário (embora eu não saiba se alguém está realmente usando).

@vnen O código-fonte em C# é usado apenas para scripts. Não é compilado em reload .

@touilleMan

Considerando este segundo ponto, o tinycc parece uma solução realmente interessante dado o seu baixo peso, o número de plataformas suportadas (x86, amd64, arm, arm64 para linux, win32 e osx), a libtcc.h api que permite integrá-lo em um programa e cruza o modo de compilação. Além disso, a versão 0.9.27 foi lançada em dezembro passado (então o projeto parece ainda vivo ^^).

Má ideia, o projeto está mais provavelmente morto do que vivo!

@neikeq

Para binários construídos com o módulo mono, você poderia escrever um compilador GDScript2IL e então teria a compilação JIT e AOT.

Mas esta é uma opção interessante. E talvez comparável aos custos trabalhistas com a adição do JIT.

Em geral, se já houver uma pessoa que estará envolvida nisso em qualquer caso, deixe-a fazer isso. A equipe não perde com isso. Proponho relaxar e esperar, de repente algo vai acontecer (embora eu não conte com isso)

@DoctorAlpaca considerando o envio do método virtual, acho que as coisas já estão assim ao usar a API C++. Então, seu objetivo aqui é tornar o GDScript mais rápido que o C++? ;-)
De qualquer forma, me desculpe se meu post anterior apareceu para você no estilo "é inútil, você não deveria fazer isso".
A experiência é imensamente mais valiosa do que a suposição, então só posso apoiá-lo, além disso, há grandes chances de que seu trabalho o leve a melhorar várias partes do mecanismo (correção de bugs, limpeza ou até mesmo encontrar otimizações para o também- sistema Variant dinâmico para ser rápido) para que nada nunca seja perdido!
BTW, se você estiver realmente interessado em JIT, sugiro que você venha ao Europython (final de julho em Edimburgo, estarei lá ^^), a equipe Pypy está lá todos os anos e você pode fazer sprint coding no Pypy com eles durante todo o fim de semana. Geralmente não há muita gente e eles são muito gentis e pedagógicos, então é basicamente como fazer uma aula de 2 dias no JIT com especialistas de classe mundial ;-)

@ret80 Concordo que o projeto tinycc não está realmente ativo. Por outro lado, estamos falando de um compilador C, o padrão mais usado ainda é o C99 (com o C89 ainda realmente presente), então não há muitas evoluções para fazer uma vez que o projeto esteja estável o suficiente (acho que poderíamos dizer que evoluções podem ser feitas em otimização, mas isso provavelmente aumentaria o tamanho e a velocidade do compilador, então não é o objetivo deste projeto).
Minha maior preocupação para este compilador é mais o suporte de arquiteturas de braço e compilação cruzada .dll e .dynlib porque eles são recursos mais exóticos (portanto, potencialmente menos testados).
Finalmente, dado que usaríamos este compilador com código gerado automaticamente (sempre as mesmas construções e gdnative como dependência única), devemos estar relativamente seguros para não encontrar bugs ocultos quando o projeto estiver no caminho certo.

@neikeq Acho muito elegante a ideia de usar o runtime mono para o JIT ! Isso manteria baixo o número de dependências (e melhoraria o número de pessoas envolvidas no módulo mono, o que é sempre melhor) e nos pouparia de fazer mais um lançamento.

Honestamente, eu não faria JIT, mas sim AOT compilar. Algo como TerraLang (um frontend de lua para LLVM projetado para construir linguagens dinamicamente) seria útil tanto para o desenvolvimento de recarregamento a quente da fonte quanto para a compilação para objetos nativos binários para vinculação de compilação (sem interpretação, LLVM puro compilado) seria útil como seria bastante trivial especializar-se até mesmo para tipos de Node específicos. A sobrecarga virtual provavelmente ainda seria um problema, mas seria muito mais rápido do que é agora, especialmente quando as informações de digitação começarem a existir. Pessoalmente, eu apenas incorporaria o LLVM no editor/compilador, sim, aumentaria o tamanho, mas também significa que os próprios jogos podem acabar ainda menores, especialmente se você incluir os arquivos de objeto e/ou fonte de godot com o editor e deixar LTO tudo em uma saída final menor e mais rápida.

O projeto luaJIT ilustra bem esses pontos: o projeto é simplesmente brilhante (base de código muito boa, desempenho muito bom), resolve um caso de uso real, é usado por empresas comerciais.
E, no entanto, está morrendo lentamente, já que seu autor principal recuou, incapaz de acompanhar a nova versão lua (luaJIT parou em 5.1, argumentando que a adição de inteiro em cima de float na linguagem feita pelo 5.2 seria muito complexa para add), mesmo que a comunidade lua tenha um tamanho médio e luaJIT seja o projeto mais conhecido.

Não tenho certeza se o chamaria de morto, foi bifurcado no github há muito tempo e está sendo atualizado para as versões modernas do LUA e assim por diante.

Acho muito elegante a ideia de usar o runtime mono para o JIT! Isso manteria baixo o número de dependências (e melhoraria o número de pessoas envolvidas no módulo mono, o que é sempre melhor) e nos pouparia de fazer mais um lançamento.

Ainda não é permitido no iOS e assim por diante.

Eu ainda acho que a ideia JIT deve ser mesclada com o GDNative de alguma forma para que o GD Script seja pré-compilado como um tipo de código de operação C de terceiros e o GDNative deve ser capaz de analisar esse código de operação e traduzido em código C++.

@OvermindDL1 muito obrigado por mencionar o TerraLang, parece um projeto muito interessante ;-)

Não tenho certeza se o chamaria de morto, foi bifurcado no github há muito tempo e está sendo atualizado para as versões modernas do LUA e assim por diante.

Eu não sou muito próximo da comunidade lua, então meu entendimento pode estar errado neste tópico. Você pode me apontar para a versão bifurcada do luaJIT implementando as novas versões do lua?

@OvermindDL1 muito obrigado por mencionar o TerraLang, parece um projeto muito interessante ;-)

Eles são muito amigáveis ​​com relações públicas, é um projeto muito legal que ainda não foi notado por muitos ainda. É muito bom criar uma linguagem dentro, analisadores integrados, integração LLVM, pode gerar binários totalmente compilados e otimizados, etc ...

Mesmo considerando que, se eu fosse escrever um compilador de backend nativo para GDScript, provavelmente usaria apenas a versão mais recente da API C++ do LLVM. A API C é estável, mas a API C++ tem muito mais poder, muito poder que é necessário para implementar uma linguagem e não apenas escanear, especialmente se você quiser usar libclang para o conjunto de otimização completo. No entanto, o TerraLang também é totalmente funcional dessa maneira e é significativamente mais fácil de usar se menos testado e usando uma versão LLVM mais antiga (API C++, embora eles queiram atualizar, então é claro que o PR é bem-vindo).

Eu não sou muito próximo da comunidade lua, então meu entendimento pode estar errado neste tópico. Você pode me apontar para a versão bifurcada do luaJIT implementando as novas versões do lua?

Eu encontrei alguns interessantes em https://github.com/LuaJIT/LuaJIT/network em um ponto, ele foi bifurcado tantas vezes e há muito trabalho de desenvolvimento acontecendo nele em alguns grandes forks. Eu também não sou muito grande na comunidade Lua, implemento luajit em alguns projetos para obter suporte a scripts, mas não escrevo lua eu mesmo (aparentemente, lua tem um gerenciador de pacotes hoje em dia, luarocks ou algo assim). Eu me deparei com um grande fork ou dois de luajit que também não é bifurcado diretamente do repositório luajit (portanto, não aparecerá nessa lista), e 'acho' que o maior que vi não estava listado nessa lista .. .

Admito que meio que perdi a noção de todas as coisas que as pessoas propõem aqui, mas parece que pelo menos alguns dizem que o GDScript precisa ser digitado estaticamente ou o JIT deve ser baseado em LLVM. Discordo totalmente dessas afirmações.

Recentemente, comecei a trabalhar em JIT para GDScript usando RPython (o kit de ferramentas que o PyPy usa para criar seu JIT) e também procurei usar o LLVM como alternativa.

Para encurtar a história: LLVM não é adequado para JIT, eu li alguns posts chegando a essa conclusão, e minhas experiências com ele também parecem confirmar isso. Há duas razões principais:

  1. LLVM é lento. Não me refiro ao código gerado pelo LLVM, quero dizer que o tempo que o LLVM leva para criar esse código cria um gargalo para um JIT. O JIT do Webkit mudou de uma solução LLVM para uma solução homebrew, e esse foi um dos motivos

  2. O LLVM assume que seu código é tipado estaticamente e é apenas um ajuste ruim para compilar linguagens tipadas dinamicamente.

É por isso que desisti do LLVM (o que é uma pena, porque gosto muito dele) e estou trabalhando com RPython. Tem prós e contras:

Os prós

  1. O RPython é perfeito: é um sistema para construir intérpretes JIT para linguagens tipadas dinamicamente. Juntamente com o Python (PyPy), as pessoas escreveram intérpretes PHP que se mostraram muito mais rápidos que o oficial, Ruby e linguagens personalizadas.

  2. O RPython é um ótimo sistema para construir JITs de rastreamento para linguagens tipadas dinamicamente. Para linguagens tipadas dinamicamente, um JIT de rastreamento parece ser a melhor solução. A implementação de um JIT de rastreamento é um trabalho árduo e requer a adição de pontos de medição ao código não jitted. O RPython existe há mais de 10 anos, tem pessoas muito inteligentes trabalhando nele (e alguns trabalhos acadêmicos foram lançados sobre o RPython) e requer que você faça muito pouco para obter um JIT em execução. Basicamente, ele espera que você escreva um interpretador de bytecode em RPython e diga a ele quais bytecodes são instruções de salto e ele faz o resto (e faz muito bem também; os primeiros experimentos sugerem uma aceleração de 40x para código pesado de computação pode ser possível)

  3. O código JITted é bastante rápido e suporta backends x86, x64 e ARM

  4. É um projeto ativo com algumas pessoas trabalhando nele e uma comunidade ativa.

  5. O interpretador de bytecode e as funções são bastante legíveis. A implementação atual em Variant_op.cpp parece horrível; não me interpretem mal, eu sei porque parece assim e que é necessário para um bom desempenho, mas hackear não é muito divertido.

Os contras

  1. (Partes de) Variant.cpp precisam ser reescritos/duplicados em RPython.

A solução de tipagem dinâmica do GDScript, a classe Variant, dificulta a criação de um bom código JITTed. Uma das coisas que o pipeline JIT faz é perceber quando uma função é frequentemente chamada com um parâmetro int, por exemplo, e depois otimiza para esse caso. Se usássemos apenas Variant como tipos opacos, os testes sugerem que só podemos obter uma melhoria de velocidade de 2x, porque tudo o que acabamos efetivamente é compilar GDScript em código C++ que efetivamente chama Variant para cada operação, como adição ou subtração.

Portanto, para obter uso real do JIT, pelo menos algumas partes do Variant devem ser duplicadas como código RPython para que o JIT saiba o que está acontecendo. Não é muito bonito, mas parece funcionar.

  1. RPython cria código C, não C++. Meio infeliz, mas podemos fazer isso funcionar, especialmente com a interface GDNative

  2. Temos código escrito em duas linguagens, C++ e RPython. No entanto, Godot já usa SCons, que é Python, então, estritamente falando, não estamos adicionando uma nova linguagem

  3. RPython não é divertido de programar. Basicamente, você acaba escrevendo em uma linguagem que é um pouco mais poderosa que C, mas não tão poderosa quanto C++, e leva algum tempo para se acostumar com as construções do Python que você pode usar e quais você não pode

  4. O processo de tradução para transformar o código RPython em C (que será o JIT totalmente funcional) leva muito tempo, e não estou exagerando. Neste estágio inicial de desenvolvimento, leva "apenas" de 1 a 2 minutos, mas espero que leve de 20 a 40m para a implementação um pouco completa. Isso não é tão horrível quanto parece, no entanto, porque o código C só precisaria ser gerado quando o próprio JIT for modificado, caso contrário, o processo de compilação Godot pode usar apenas os arquivos C gerados.

Espero obter um PoC funcionando em 4-8 semanas para obter mais feedback.

@baekdahl Leitura demais. O GDScript será digitado independentemente do que você pensa. A decisão veio do alto e vai acontecer em um futuro próximo.

@CarlGustavAlbertDwarfsteinYung GDScript está se tornando opcionalmente digitado. Grande diferença para este problema.

Para encurtar a história: LLVM não é adequado para JIT, eu li alguns posts chegando a essa conclusão, e minhas experiências com ele também parecem confirmar isso. Há duas razões principais:

Isso porque o JIT deve estar em tempo de desenvolvimento. Ao fazer um lançamento, uma compilação nativa estática completa deve acontecer. O LLVM pode não ser o JIT mais rápido, mas é um JIT, além de poder compilar totalmente nativo para bibliotecas/objetos/programas independentes.

O LLVM assume que seu código é tipado estaticamente e é apenas um ajuste ruim para compilar linguagens tipadas dinamicamente.

Mmm, na verdade ele tem algumas interfaces fantásticas de tipagem dinâmica.

Independentemente disso, se o GDScript está sendo digitado, isso não é um problema de qualquer maneira, as partes 'dinâmicas' do GDScript se tornariam uma variante de qualquer maneira.

É por isso que desisti do LLVM (o que é uma pena, porque gosto muito dele) e estou trabalhando com RPython. Tem prós e contras:

@brakhane É capaz de compilação nativa? Se não for, parece um não-inicial, pois você não pode JIT em plataformas como iOS, além de que um JIT em tempo de execução retardará o carregamento e o spool-up onde o AOT não tem esse custo nos lançamentos.

GDScript está se tornando opcionalmente tipado. Grande diferença para este problema.

Sim, mas mesmo as partes 'não tipadas' são variantes, o que é bastante trivial para representar no LLVM.

Isso porque o JIT deve estar em tempo de desenvolvimento.

Então não é JIT, mas compilação normal antes do tempo.

É capaz de compilação nativa? Se não for, parece um não-inicial, pois você não pode JIT em plataformas como iOS, além de que um JIT em tempo de execução retardará o carregamento e o spool-up onde o AOT não tem esse custo nos lançamentos.

Não, é um JIT. E se o iOS não permitir JITs, não funcionará lá. 🤷

Sim, mas mesmo as partes 'não tipadas' são variantes, o que é bastante trivial para representar no LLVM.

Mas você não obtém tanta melhoria de velocidade com isso, como mencionei.

É bastante fácil compilar o GDScript assim:

while i<10:
    foo(i)
    i = i + 1

para algo equivalente ao seguinte C++

// i, foo are Variant
while (i.op("<", Variant_10_const)) {
    foo.call(i);
    i = i.op("+", i, Variant_1_const);
}

(ou algo assim, não me lembro da API Variant exata, já faz algum tempo)

Mas isso só vai te dar uma melhoria de 2 vezes na velocidade. Transformar em while (i<10) {foo.call(new Variant(i)));i += 1} é muito mais difícil de conseguir.

Mmm, na verdade ele tem algumas interfaces fantásticas de tipagem dinâmica.

Eu adoraria ouvir sobre isso.

Então não é JIT, mas compilação normal antes do tempo.

Um JIT pode não produzir código em geral tão rápido quanto o AOT, mas é muito mais rápido durante o desenvolvimento, então ainda é útil lá. No entanto, eu seria muito bom com AOT, mesmo em tempo de desenvolvimento, pois se os módulos individuais forem compilados separadamente, não será um problema de velocidade (<1s fácil).

Não, é um JIT. E se o iOS não permitir JITs, não funcionará lá.

Ah sim isso não seria útil então. O iOS requer um interpretador lento completo ou requer AOT. Além disso, o AOT carrega mais rápido, requer um tempo de execução menor, pode executar LTO, etc...

Mas você não obtém tanta melhoria de velocidade com isso, como mencionei.

Exceto Typed GDScript será capaz de inferir 'a maioria' dos tipos independentemente (pode até ser capaz de decorar a variante nas chamadas get_node para criar um caminho feliz para o tipo de nó esperado com base na cena com fallback de variante padrão ). No entanto, na grande maioria dos casos, mesmo em código não tipado, ainda será capaz de inferir a grande maioria dos tipos, obtendo assim melhorias significativas de desempenho e redução de código gerado.

(ou algo assim, não me lembro da API Variant exata, já faz algum tempo)

Eh, isso não é nem remotamente como eu sugeriria fazê-lo honestamente. A execução de uma operação < nativa por meio de um visitante teria um desempenho significativamente maior do que o teste de string como apenas um exemplo. No entanto, não tenho certeza se este exemplo é muito válido, pois imagino que o tipo de i seria inferido como inteiro acima de onde está definido (código não mostrado) e mesmo que não seja, poderia seja uma verificação de tipo único antes do loop para verificar se funciona com tais comparações integrais e se não lançar uma exceção ou erro de alguma outra forma, caso contrário adquira o valor integral/flutuante da variante (provavelmente convertido para algo útil para minimizar a geração de código, o LLVM já possui passes para otimizar isso) e o loop se tornaria totalmente otimizado independentemente. Você deseja minimizar completamente o tempo de variante e fazer verificações de acesso antecipado e frequente. Voltar para os visitantes também não é tão lento, especialmente porque a maioria dos visitantes apenas lançaria um erro (como chamar um método em um inteiro).

Mas isso só vai te dar uma melhoria de 2 vezes na velocidade. Transformá-lo em um while (i<10) {foo.call(new Variant(i)));i += 1} é muito mais difícil de conseguir.

Isso parece bastante trivialmente otimizável? Tudo seria bastante nativo até chamar um trampolim para foo despachar apropriadamente (função de wrapper simples para executar o tipo de despacho ou erro sem tipo), mas mesmo assim se o tipo de foo é conhecido (possível também inferível se não for explicitamente digitado), então isso também seria otimizado.

Eu adoraria ouvir sobre isso.

Além do JIT de geração de código nativo inline que o próprio LLVM possui (o que funcionaria perfeitamente com todos os itens acima e seria fantástico para o desenvolvimento de código de edição durante a execução enquanto ainda é capaz de fazer AOT para versões) que permite uma Tradução JIT, também existem bibliotecas como pyston, fastlua, entre outras construídas em LLVM que possuem várias formas de rastreamento de JITs para melhoria sob demanda, que eu pessoalmente acho bastante inútil para tempo de desenvolvimento e você não pode usar em uma versão confiável tempo de qualquer maneira por causa de plataformas como iOS, então não vejo o ponto.

@OvermindDL1 Você ainda está falando sobre AOT que é acionado automaticamente ao salvar um GDScript editado.

E sim, se o GDScript obtiver digitação estática opcional, as partes em que os tipos são impostos podem ser convertidas em código de máquina bastante eficaz. O GDScript também é bastante limitado com o que pode fazer, então não acho que torná-lo estritamente estático em todos os lugares não seria uma grande perda.

E se as pessoas quiserem implementar tal coisa, elas podem. Eu já escrevi alguns compiladores estáticos e esse problema não é interessante para mim.

O que é interessante para mim é criar um JIT para um GDScript tipado dinamicamente que seja executado em PCs. Seria bom se outras pessoas acabassem usando também, mas não é um requisito para mim. Eu aprecio o aviso "você pode estar perdendo seu tempo".

Eh, isso não é nem remotamente como eu sugeriria fazê-lo honestamente. A execução de uma operação < nativa por meio de um visitante teria um desempenho significativamente maior do que o teste de string como apenas um exemplo.

IIRC, não é realmente um "<", mas mais um parâmetro Operation.LT que é fornecido.

Isso parece bastante trivialmente otimizável?

Não é, a menos que você queira reimplementar um novo interpretador GDScript completo. E mesmo assim, você terá dificuldade em inferir tipos, exceto em casos triviais.

Se você simplesmente deseja substituir a parte do interpretador de opcode, não é tão trivial (fato curioso: os arquivos ".gdc" não são bytecode, mas mais parecidos com o código-fonte tokenizado que é interpretado e transformado em bytecode na memória posteriormente). Neste momento, você sabe o seguinte:

  • Há uma variável i, que é comparada ("menor que") a outra variante cujo conteúdo é opaco (embora você possa usar a API interna para ver que é um inteiro com valor 10)
  • Há outra variável "foo", que é chamada com i como parâmetro
  • i recebe um novo valor, o resultado de adicionar i a outra variante opaca

Se você quiser fazer a compilação AOT, basicamente terá que reimplementar todo o interpretador GDScript. O que não é tão trivial quanto parece, pois uma parte significativa do interpretador faz parte da classe Variant. E Variant é o que mantém Godot juntos, mesmo as partes que não são GDScript dependem fortemente do Variant. Então, quando você começa a mexer com isso, de repente corre o risco de quebrar a compatibilidade com versões anteriores da interface GDNative.

Realmente não é tão fácil quanto parece à primeira vista; Também fiquei surpreso quando mergulhei nele pela primeira vez.

Você ainda está falando sobre AOT que é acionado automaticamente ao salvar um GDScript editado.

Não em todos os lugares, apenas no Export. Agora, eu preferiria que fosse AOT ao salvar, mas não precisa ser, você poderia apenas AOT no Release/Export e JIT ao executá-lo através do editor ou então para permitir a troca a quente do código durante o tempo de execução.

E sim, se o GDScript obtiver digitação estática opcional, as partes em que os tipos são impostos podem ser convertidas em código de máquina bastante eficaz. O GDScript também é bastante limitado com o que pode fazer, então não acho que torná-lo estritamente estático em todos os lugares não seria uma grande perda.

Com base na especificação atual, há muita inferência, 'a maioria' do código deve, em geral, ser digitado automaticamente por inferência. Estou bastante animado com isso. (Sou fã da digitação HM, implementei-a uma dúzia de vezes de várias maneiras ao longo dos anos!)

O que é interessante para mim é criar um JIT para um GDScript tipado dinamicamente que seja executado em PCs. Seria bom se outras pessoas acabassem usando também, mas não é um requisito para mim. Eu aprecio o aviso "você pode estar perdendo seu tempo".

Sim, um JIT absolutamente não deve ser obrigatório, pois isso significa que não seria exportável para todas as plataformas suportadas (além de adicionar sobrecarga). AOT ou interpretação deve ser sempre o método principal, e considerando que a maioria das plataformas que não suportam JIT (iOS, webassembly, etc...) também tendem a ser as plataformas menos eficientes, então AOT deve ser absolutamente o padrão , não interpretação, o que é ainda melhor porque a digitação está sendo adicionada.

Não é, a menos que você queira reimplementar um novo interpretador GDScript completo. E mesmo assim, você terá dificuldade em inferir tipos, exceto em casos triviais.

Não estou falando de interpretação, estou falando de compilação. Se um código totalmente não tipável for compilado, ele se tornará apenas variantes no código de máquina (tipo de soma marcada), e qualquer acesso a ele, como digamos, uma função operator< apenas executaria uma chamada de visitante, o que envolve um único salto estático com base no valor do tipo dinâmico e, em seguida, a execução do código estático (que, se for, digamos, um inteiro é embutido, se for, digamos, um tipo personalizado, isso provavelmente acabaria realizando uma chamada virtual, ou mesmo uma chamada estática se for um tipo grande implementado diretamente na variante como um vetor).

Se você simplesmente deseja substituir a parte do interpretador de opcode, não é tão trivial (fato curioso: os arquivos ".gdc" não são bytecode, mas mais parecidos com o código-fonte tokenizado que é interpretado e transformado em bytecode na memória posteriormente).

De forma alguma, os opcodes não devem existir no momento em que o AOT estiver completo, deve ser um código de máquina puro reduzido para o maior despacho de variantes no código de máquina ou para o menor código de máquina eficiente perfeitamente conhecido e tipado.

Há uma variável i, que é comparada ("menor que") a outra variante cujo conteúdo é opaco (embora você possa usar a API interna para ver que é um inteiro com valor 10)

Se o tipo da variável i for conhecido, você poderá gerar um código de máquina que teste o tipo da variante opaca e, se não for uma correspondência compatível imediata, ocorrerá um erro imediato, caso contrário, execute a comparação com base no i tipo. Se o tipo da variável i não for conhecido, será um despacho completo do visitante nas duas variantes (uma chamada de função conhecida estaticamente) para adquirir as tags de tipo das variantes e pular para o código de máquina apropriado para os tipos dados.

Há outra variável "foo", que é chamada com i como parâmetro

Se o tipo de chamada de foo for conhecido, como um exemplo em notação ML, int -> int , se o tipo de i também for conhecido e também for um int, execute eficiente código de máquina, se o tipo de i não for conhecido, teste a tag de tipo se corresponder, se não corresponder, erro, se houver, extraia o valor e execute um código de máquina eficiente. Se o tipo de chamada de foo não for conhecido mas a etiqueta de tipo indicar que pode ser chamada (erro else) então se o tipo de i for conhecido (para todas as definições de known aqui quero dizer ambos conhecidos em tempo de compilação e, consequentemente, desembalados), em seguida, envolva-o em uma variante (configuração em linha eficiente da tag de tipo apropriado e colocando os bits de i nos bits apropriados) e execute uma chamada de variante para a função de trampolim encapsulada, que então verifica os argumentos e chama a função interna real (que teria sido chamada diretamente se o tipo de chamada de foo fosse conhecido estaticamente).

i recebe um novo valor, o resultado de adicionar i a outra variante opaca

Tudo depende de qual é o tipo estático conhecido de i agora e execute a geração de código nativo descrito acima conforme necessário.

Se você quiser fazer a compilação AOT, basicamente terá que reimplementar todo o interpretador GDScript.

O que ainda é menos trabalhoso do que criar um JIT especificamente para GDScript, a menos que você esteja encapsulando um JIT existente (do qual as operações esperadas podem não corresponder perfeitamente ao GDScript). A geração de AOT em si não é muito mais difícil do que criar um interpretador, especialmente no LLVM, pois faz o trabalho significativamente difícil para você (otimizações, LTO, geração de código de máquina, depurador, etc... etc...).

O que não é tão trivial quanto parece, pois uma parte significativa do interpretador faz parte da classe Variant.

Mas ainda não tão difícil quanto implícito (escrevi uma variedade de linguagens ao longo das décadas, muitas até código de máquina, tanto diretamente para x86 quanto via LLVM).

E Variant é o que mantém Godot juntos, mesmo as partes que não são GDScript dependem fortemente do Variant. Então, quando você começa a mexer com isso, de repente corre o risco de quebrar a compatibilidade com versões anteriores da interface GDNative.

É exatamente por isso que as variantes devem ser inseridas diretamente no código da máquina. Nenhum interpretador de opcode ou qualquer coisa do tipo, em vez disso, faça um despacho completo e adequado de visitantes (seja por meio de um salto local baseado na tag de tipo ou mesmo por meio de uma chamada longa de uma matriz estática (tabela de despacho)). Isso não apenas manteria a compatibilidade com versões anteriores, mas também permitiria que a interface GDNative carregasse diretamente as bibliotecas totalmente compiladas AOT (ou se Godot obtiver a capacidade de LTO o mecanismo para as interfaces GDNative por meio de objetos binários, as otimizações LTO poderiam ser aplicadas, o que poderia em seguida, reduza significativamente o tamanho da saída do programa final, pois o código não utilizado conhecido pode ser removido, embora isso seja uma tarefa posterior).

Realmente não é tão fácil quanto parece à primeira vista; Também fiquei surpreso quando mergulhei nele pela primeira vez.

Certamente é uma quantidade de trabalho não trivial e, na verdade, a maior parte é uma grande quantidade de trabalho ocupado para traduzir o pipeline de compilação GDScript anterior para gerar o código LLVM Assembler (do qual ele possui uma API fantástica para fazê-lo) em vez de códigos de operação. Mas é totalmente factível, e em não tanto tempo quanto se poderia esperar, dada uma implementação de linguagem.

BTW, todos os operadores Variant são tratados aqui: https://github.com/godotengine/godot/blob/d2b75557a5dedf951ee036ca01af4f94bc059069/core/variant_op.cpp#L391 -L392

O que já está bastante otimizado.

BTW, todos os operadores Variant são tratados aqui:

Ah legal, então ele já está usando um computador goto, poderia apenas usar isso então (embora provavelmente gostaria de jogar isso no LLVM também para que ele possa inserir caminhos de chamada conhecidos quando possível, mas sim, exatamente algo assim, isso é muito um visitante variante e seria usado no pior caso, quando nenhum tipo é conhecido. No entanto, se algo for conhecido sobre os tipos, seria ainda mais rápido fazer chamadas mais diretamente otimizadas e, é claro, se todos os tipos forem conhecidos ou há apenas uma única ramificação válida, então o código de máquina não variante otimizado pode ser gerado (por meio da extração dos dados da variante (mais provavelmente passando na seção de bits convertidos) ou apenas usando os dados já em registradores/pilha/heap se local ou já extraído).

Mas você não obtém tanta melhoria de velocidade com isso, como mencionei.

É bastante fácil compilar o GDScript assim:

enquanto eu <10:
foo(i)
e = e + 1

para algo equivalente ao seguinte C++

// i, foo são variantes
while (i.op("<", Variant_10_const)) {
foo.call(i);
i = i.op("+", i, Variant_1_const);
}

(ou algo assim, não me lembro da API Variant exata, já faz algum tempo)

Mas isso só vai te dar uma melhoria de 2 vezes na velocidade.

Então, ele já não tornaria o GDScript compilado mais rápido que o C# na maioria dos casos?
Melhor desempenho com uma linguagem mais simples parece um bom negócio para mim.

Então, ele já não tornaria o GDScript compilado mais rápido que o C# na maioria dos casos?

Provavelmente não, já que teria que passar por toda a camada Variant de indireção. Olhando para linguagens como Wren, parece que usar NaN-tagging ou tipos unboxed é mais prejudicial ao desempenho do que compilar coisas em código de máquina. C# tem ambos os tipos unboxed _e_ compilação JIT, então compilar GDScript para código de máquina não o tornará magicamente mais rápido que C#.
Observe que o GDScript já possui um despachante de bytecode bastante otimizado, ter código de máquina para fazer o mesmo não aceleraria o gargalo.

Bem, talvez uma boa solução seria algo que gerasse o código c++ não otimizado a partir do gdscript (talvez o arquivo sconscript também) e então o usuário otimizaria (não deve ser grande coisa, já que o usuário escreveu o script original) e compilar manualmente.

Não é perfeito, mas já é mais rápido do que fazer tudo à mão.

Se você vai escrever o código duas vezes, por que não escrevê-lo diretamente em C++?

Essa é outra coisa sobre traduzir GDScript diretamente para LLVM, pode AOT um binário para qualquer plataforma, não apenas na que você está atualmente e compiláveis ​​cruzadas.

Esse problema me lembra a linguagem Boo . Uma linguagem inspirada em python rodando em .NET.

Eu não sei o que vale (não usei muito tbh), e o projeto parece morto (5 anos? Droga...) mas estou jogando de qualquer maneira.

Eu prefiro a declaração de tipo do Kotlin. É mais legível

var name:String = "Bob"

@Logmytech Você está meio ano atrasado, o GDScript digitado já está no mestre (e acontece de usar declarações de tipo Kotlin-ish).

Ruby tem um novo compilador JIT: https://bugs.ruby-lang.org/projects/ruby/wiki/MJIT#MJIT -organization

Apenas curioso, isso está planejado para 4.0?

@girng , há uma suspeita de que isso não acontecerá no Godot 4.0. Mas não é exatamente :)

Acho que a v4.0 poderia ter transpilação GDScript para C preliminar, assim como a v3.0 tinha suporte preliminar a C#.
(se tivermos sorte).

É mais fácil fazer suporte para a linguagem ZIG . Linguagem promissora. Embora eu goste muito do GDScript, e se funcionasse mais rápido seria muito legal. Aliás, seria ótimo se não fosse o arquivo em si ser uma classe; a classe foi escrita no arquivo, como é feito em outras linguagens. Devido a esta característica, surgem algumas dificuldades que poderiam ser facilmente contornadas.

O Godot 4.0 provavelmente não terá JIT, mas terá melhorias de desempenho no GDScript.

Eu fiz um trabalho significativo para compilar GDScript para C com GDNative. Cheguei a um ponto em que não estou convencido de que seja um esforço que valha a pena completá-lo. Teoricamente, o desempenho do código AOT pode ser muito mais rápido do que o GDScript na execução, mas na prática os resultados são mais decepcionantes. Os custos de passar pela camada GDNative são significativos, e sem grandes mudanças no próprio GDScript o código compilado é muito frágil (o bytecode GDScript não é gerado de uma forma que seja segura para uma execução posterior porque espera que o ambiente seja estático , incluindo posições de coisas nas matrizes internas do mecanismo).

No entanto, estou convencido de que, implementando uma quantidade mínima de otimizações de back-end, algumas melhorias reais de desempenho podem ser realizadas.

Estou trabalhando em uma bifurcação que inclui várias otimizações que não exigem nenhuma alteração significativa no GDScript, mas já geram alguns ganhos de desempenho.

Até agora, implementei as estruturas de dados padrão para realizar a otimização de código, bem como alguns passos básicos de otimização:

  • construção e destruição do gráfico de fluxo de controle
  • análise de fluxo de dados, incluindo gens e conjuntos de eliminação, intervalos ao vivo
  • eliminação de código morto
  • jump threading (otimização importante), pois o GDScript cria muitas ramificações desnecessárias

Como um POC, pretendo implementar mais algumas passagens antes de empurrar meu fork:

  • eliminação de subexpressão comum
  • inferência de tipo de temporários e aritmética nativa para temporários (também não estou convencido de que a aritmética digitada será uma grande vitória)

As operações aritméticas tipadas requerem modificações nas classes GDScriptFunction e de estado de função para incluir arrays de registradores tipados, bem como novos opcodes para endereçar registradores tipados e evitar verificação de tipo adicional em tempo de execução.

Você pode ver meu progresso nas otimizações mencionadas acima no meu godot fork, https://github.com/pchasco/godot/tree/gdscript-optimization/modules/gdscript/optimizer

@pchasco Você pode querer falar com @vnen , que atualmente está retrabalhando o GDScript para Godot 4.0 - IMO, este seria um bom momento para ter certeza de obter frutos tão fáceis :)

Veja https://godotengine.org/article/gdscript-progress-report-writing-tokenizer

@pchasco Você pode querer falar com @vnen , que atualmente está retrabalhando o GDScript para Godot 4.0 - IMO, este seria um bom momento para ter certeza de obter frutos tão fáceis :)

Veja https://godotengine.org/article/gdscript-progress-report-writing-tokenizer

http://blog.moblcade.com/?p=114

Bem, a VM vai mudar para integrar as instruções digitadas, então não tenho certeza se fazer o trabalho de otimização agora é o melhor. Eu acredito que essas ideias podem ser usadas na nova VM, mas vai demorar um pouco até que seja concluída.

Não tenho certeza se algum dia teremos JIT, então talvez essa questão possa ser encerrada em favor de outra coisa. Há uma ideia de fazer uma compilação AOT que seria mais fácil de gerenciar do que isso.

@vnen Sim, o título do problema não se encaixa mais, se você ler o acima, eles acharam que o JIT não vale a pena e, em vez disso, foram para otimizações direcionadas no interpretador atual.

Pode valer a pena manter isso em mente enquanto refaz isso, mas sim, você saberá muito melhor do que eu quando for um bom momento para ver sobre isso :) só queria conectar vocês dois

http://blog.moblcade.com/?p=114

@pchasco Você pode querer falar com @vnen , que atualmente está retrabalhando o GDScript para Godot 4.0 - IMO, este seria um bom momento para ter certeza de obter frutos tão fáceis :)
Veja https://godotengine.org/article/gdscript-progress-report-writing-tokenizer

http://blog.moblcade.com/?p=114

Vejo que encontrou meu blog!

Bem, a VM vai mudar para integrar as instruções digitadas, então não tenho certeza se fazer o trabalho de otimização agora é o melhor. Eu acredito que essas ideias podem ser usadas na nova VM, mas vai demorar um pouco até que seja concluída.

Não tenho certeza se algum dia teremos JIT, então talvez essa questão possa ser encerrada em favor de outra coisa. Há uma ideia de fazer uma compilação AOT que seria mais fácil de gerenciar do que isso.

Concordo que o JIT não é uma solução viável devido ao número de plataformas, hardware variável e quantidade de trabalho necessária para manter. AOT é definitivamente a solução mais simples e portátil para desempenho nativo com GDScript. Na verdade, o Unity também transpila .NET para C++ para seu AOT. Eu estaria interessado em contribuir com a reescrita do GDScript.

@pchasco sugiro entrar no canal de IRC #godotengine-devel e entrar em contato por lá :)

Como mencionei antes, é muito improvável que adicionemos JIT. Há algumas outras idéias na discussão, mas nenhuma conclusão.

Como estamos movendo a proposta para o rastreador GIP, vou fechar esta e se alguém tiver uma ideia, por favor abra uma nova proposta (depois de verificar se ninguém fez isso primeiro). Esta questão pode ser vinculada em propostas relevantes para manter o histórico da discussão.

Alguns contribuidores têm ideias diferentes para a compilação AOT, mas vou deixá-los abrir uma proposta com o que eles têm em mente.

@vnen
Então, por favor, atualize o Roteiro, está enganando as pessoas.

Bom, até esqueci que existia o roadmap, faz muito tempo que não é atualizado.

@vnen
Você esqueceu, mas outros usuários não. Então, por favor, atualize-o, porque muitos estão mantendo isso em mente.

intérpretes são lixo e para mim isso mata a utilidade que o script gd tem quando o desempenho é péssimo

você usa um interpretador quando está escrevendo um emulador básico, não um mecanismo de jogo completo

um compilador just in time pode muitas vezes ser mais rápido do que um compilador ass lento e desatualizado de qualquer maneira, o sistema interpretador é simplesmente ruim, e eu sinto que prefiro arrancar o script gd e ir puramente com c ++, e apenas fazer um configuração do gueto com um sistema just in time

acho que as pessoas não estão dispostas a admitir que o intérprete é uma porcaria já que as pessoas não querem ferir os sentimentos umas das outras por algum motivo, mas comparado ao c++, o resultado final é bonito, ehhh...

é... ok, se você tem algo realmente básico acontecendo, eu acho, mas, para algo sério, é apenas, como se não fosse adequado para muita coisa na minha mente, além de jogos realmente simples

@RaTcHeT302 não há necessidade de trazer sua negatividade e lembre-se de que temos um Código de Conduta . Existem planos para otimizar o GDScript de várias maneiras, incluindo compilação para código nativo, mas não com JIT. Além disso, se você comparar com o C++, mesmo um tempo de execução JITed será lento.

@vnen : Apenas dizendo que LuaJIT está quase lá com C/C++ em benchmarks de desempenho - mas há uma crença generalizada de que a mágica está envolvida :smile:

JIT não é possível em todas as plataformas principais. É melhor concentrar esforços
onde todos podem aproveitá-los. A compilação antecipada é a única
Tecnologia 100% compatível.

Em dom, 9 de agosto de 2020 às 08:46 Zireael07 [email protected] escreveu:

@vnen https://github.com/vnen : Apenas dizendo que o LuaJIT está quase pronto
lá com C/C++ em benchmarks de desempenho - mas há uma ampla
crença de que a magia está envolvida 😄


Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-671047838 ,
ou cancelar
https://github.com/notifications/unsubscribe-auth/AAUFIAGK3S7RA3DRXW6NUC3R72LCVANCNFSM4CFZMZPA
.

Não há desvantagens no AoT em comparação com o JIT? Por exemplo, e quanto a scripts gerados ou carregados dinamicamente? JIT não deve ter problemas para otimizá-los, mas AoT?

A propósito, em qual plataforma principal o JIT não é possível? Eu pensei que o Java com seu JIT fosse executado praticamente em todos os lugares.

Edit: Ah, acho que a Apple é o problema, que proíbe o uso do JIT. Portanto, é uma limitação arbitrária de bloqueio de fornecedor, não técnica. Ainda bem que não tenho que suportar nenhum dispositivo da Apple ...

iOS não permite JIT para aplicativos distribuídos pela App Store
a menos que esteja usando o componente do navegador da Apple.

AOT não seria possível para scripts dinâmicos.

Em dom, 9 de agosto de 2020 às 11h15 monnef [email protected] escreveu:

Não há desvantagens no AoT em comparação com o JIT? Por exemplo o que dizer
scripts gerados ou carregados dinamicamente? JIT não deve ter um problema
otimizando-os, mas AoT?

A propósito, em qual plataforma principal o JIT não é possível? Eu pensei Java
com seu JIT é executado praticamente em todos os lugares.


Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-671064117 ,
ou cancelar
https://github.com/notifications/unsubscribe-auth/AAUFIACWWCWDXO4NZ7GCL63R724RHANCNFSM4CFZMZPA
.

iOS, Web e algumas plataformas de console não suportam a compilação JIT.

Há um boato de que o AOT agora está fora do baralho com o JIT. Espero que isso não seja verdade. Alguém, por favor, me diga, é verdade?

Você está dizendo que há um boato de que o AOT não seria mais permitido em alguns
dispositivos? Isso é literalmente impossível, a menos que eu esteja entendendo mal alguma coisa?

Em quarta-feira, 30 de setembro de 2020 às 05:47 Teashrock [email protected] escreveu:

>
>

@Calinou https://github.com/Calinou @neikeq https://github.com/neikeq
@reduz https://github.com/reduz @akien-mga
https://github.com/akien-mga


Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-701313134 ,
ou cancelar
https://github.com/notifications/unsubscribe-auth/AAUFIADVHAXMGBOL3YEDVILSIMEEFANCNFSM4CFZMZPA
.

@pchasco
Não. Quer dizer, alguém me disse que GDScript não terá AOT nem JIT.

Talvez não seja oficialmente suportado, mas posso confirmar que o novo
A arquitetura GDScript descrita por vnen é capaz de suportá-la. Um
desenvolvedor empreendedor poderia implementar um módulo personalizado para fazer AOT para
GDNative C.

Em quarta-feira, 30 de setembro de 2020 às 11:16, Teashrock [email protected] escreveu:

>
>

@pchasco https://github.com/pchasco

Não. Quer dizer, alguém me disse que GDScript não terá AOT nem JIT.


Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-701494412 ,
ou cancelar
https://github.com/notifications/unsubscribe-auth/AAUFIACAKZVJQK2CHTDJT63SINKXDANCNFSM4CFZMZPA
.

@pchasco

pode ser

Então, você não sabe. "Capaz de apoiar" não significa "vai apoiar".

@Teashrock Não sabemos se o GDScript apresentará compilação JIT ou AOT em 4.0, mas não é muito provável. Talvez em 4.1 ou posterior…

Se você precisar de maior desempenho para fins de processamento de números (ou seja, suas limitações de CPU vêm claramente da linguagem de script), use C#. Mas primeiro, leia os tutoriais de otimização :slightly_smiling_face:

@Calinou
Só que falaram do 4.0 sobre JIT/AOT. E agora você está me dizendo sobre 4.1. Por que isso? Como é isso? Alguém, apenas dê a todos uma resposta pública clara, que não estará em algum lugar profundo no GitHub.

Agradeço antecipadamente.

Esta página foi útil?
0 / 5 - 0 avaliações