Godot: Mejora del rendimiento de GDScript a través de la compilación Just-in-time (JIT)

Creado en 5 jun. 2016  ·  105Comentarios  ·  Fuente: godotengine/godot

¿Qué hay de adoptar la compilación justo a tiempo (JIT) para GDscripts?

La compilación JIT no tiene desventajas sobre el código interpretado (aparte de un inicio más lento, en algunos casos) y muchas ventajas, principalmente en términos de rendimiento.

Para la compilación JIT, existen al menos dos formas populares de implementarla de forma multiplataforma sin escribir ningún código desde cero:

También existe la opción de usar PyPy, JVM o CLR, pero esos son bastante más pesados, usando GNU Lightning o libjit (no estoy seguro si la licencia es compatible con la de Godot) y por supuesto, escribiendo un generador de código desde cero (que podría tomar años).

Entonces, la elección es más o menos entre LLVM IR y DynASM . Ambos tienen un rendimiento muy bueno, pero DynASM está escrito en Lua y LLVM ocupa un espacio bastante grande (~20 MB), pero también ofrece otras funciones.

Por supuesto, compilar GDScript directamente en C++ también podría ser una alternativa válida: el único motor de juegos que lo hace que yo sepa (si lo entendí correctamente) es Enigma , que compila su propio lenguaje de secuencias de comandos (EDL) en C++.

¿Qué piensas sobre esto?

archived discussion feature proposal gdscript

Comentario más útil

Estoy trabajando en la compilación JIT para mi tesis de maestría y planeo implementar algo adecuado para Godot. Hasta ahora, en su mayoría solo he leído muchos artículos sobre el tema, específicamente sobre cómo lidiar con la naturaleza de escritura dinámica de GDScript. Ahora comencé a trabajar en un prototipo y espero tener algo que mostrar relativamente pronto.

Todos 105 comentarios

La compilación JIT no tiene desventajas sobre el código interpretado

Hay uno: la portabilidad…

Pero, ¿no sería mejor y más fácil implementar el tipeo estático al menos por ahora? ¿Sin mencionar el soporte de C# que eventualmente puede suceder?

@Calinou La portabilidad no es una gran preocupación, ya que podríamos simplemente ejecutar GDScript como lo hacemos ahora en plataformas no compatibles.

la escritura estática también se puede dejar de lado opcionalmente

La portabilidad de

LLVM IR en realidad haría que la tipificación estática fuera más fácil de adoptar (en el back-end, por supuesto, GDScript aún necesitaría inferencia de tipo o firmas de tipo explícitas).

Si se implementa la compatibilidad con C# mediante la vinculación con Mono o CoreCLR, ambos incluyen JIT, por lo que este problema se resolverá automáticamente. Puedo entender por qué C# (es un lenguaje mejor que Java y Unity ya lo usa), pero para esos propósitos, JVM probablemente brindaría un mejor rendimiento que Mono o CoreCLR (especialmente debido al recolector de basura JVM, que es muy adecuado para juegos ya que no congela nada). Después de Java 8, personalmente no creo que la mayoría de la gente se oponga a él en lugar de C# (ya que las ventajas que ofrece C# en este punto se reducen principalmente a Generics y LINQ, ambos no tan útiles en el desarrollo de juegos, que en su mayoría es imperativo) , y el uso de JVM brinda acceso a muchos lenguajes funcionales más ordenados que C # como Clojure, Scala y Kotlin de todos modos.

Además, LLVM hace posible el uso de C++ de forma JIT. Unreal Engine también usa C++ para secuencias de comandos, por lo que no creo que sea demasiado extraño (siempre que las fallas de segmento sean más comunes, pero GDScript aún podría ofrecerse a programadores menos experimentados). C ++ 14 (compatible con LLVM en su mayor parte) es un lenguaje bastante pequeño incluso en comparación con Java y C #, si se usa correctamente (por supuesto, los tiempos de compilación son largos y los mensajes de error aún no son tan buenos).

Apoyaría la compilación JIT para GDscript siempre y cuando no signifique el final del lenguaje (o su respectivo editor incorporado) tal como está ahora,

Mi propuesta.

  • Agregue un nuevo tipo de variable estática , que a su vez se puede crear haciendo algo como
    static var float(5.5) o algo así (la diferencia central sería un mayor rendimiento y un mejor análisis debido a la expectativa de que sea un tipo específico. Este método debería permitir el uso de escritura estática manteniendo las ventajas de la escritura dinámica y eliminar cualquier necesidad para reescribir guiones si quieres usarlos
  • Después de eso, agregue una rutina de compilación JIT que convierta GDscript a C++ en segundo plano (es decir, invisible para el usuario), pero visible en forma de rendimiento cuando se ejecuta el juego. La idea de usar C++ aquí significaría que los desarrolladores de Godot no tendrían que agregar un montón de nuevas bibliotecas al código fuente.

JIT en lenguajes tipeados dinámicamente como Lua se hace haciendo inferencia de tipos
desde el punto de entrada a través del árbol de llamadas, yendo lo más profundo posible. estoy
No digo que esto no sea posible en Godot, ya que casi todas las entradas
se escribe el punto. La finalización de código en Godot hace algo similar, que
es por eso que es capaz de adivinar tanto tipo de información.

Sin embargo, hay muchos casos en los que el tipo no es obvio, y aunque el
la finalización del código funciona muy bien adivinando las llamadas get_node() en el árbol
siendo editado, esta información puede ser diferente o cambiar en tiempo de ejecución.
Honestamente, no estoy seguro de cuán eficiente podría ser la inferencia de tipos.

Para ayudar en esto, sería fácil permitir que GDScript permitiera al usuario
"forzar" el tipo de una variable (algo que no es posible en lua). pero si tu
va a hacer esto, entonces también podría escribir completamente estático.

No estoy convencido de ningún enfoque en este momento.

El domingo 5 de junio de 2016 a las 5:37 p. m., Ace-Dragon [email protected] escribió:

Apoyaría la compilación JIT para GDscript siempre que no signifique que
final del idioma (o su respectivo editor incorporado) tal como está ahora,

Mi propuesta.

  • Agregue un nuevo tipo de variable _static_, que a su vez puede ser creado por
    manera de hacer algo como
    static var float (5.5) o algo así (la diferencia central sería
    mayor rendimiento y mejor análisis debido a la expectativa de que sea un
    tipo específico. Este método debería permitir el uso de tipeo estático mientras
    mantener las ventajas de la escritura dinámica y eliminar cualquier necesidad de
    reescribiendo guiones si quieres usarlos
  • Después de eso, agregue una rutina de compilación JIT que convierta GDscript a
    C++ detrás de escena (es decir, invisible para el usuario), pero visible en el formulario
    de rendimiento cuando se ejecuta el juego. La idea de usar C++ aquí significaría
    que los desarrolladores de Godot no tendrían que agregar un montón de nuevas bibliotecas a
    la fuente.


Estás recibiendo esto porque comentaste.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-223836149 ,
o silenciar el hilo
https://github.com/notifications/unsubscribe/AF-Z2znHs58L-KDgtIjHYYjgHVUGXgwpks5qIzOngaJpZM4IuWZe
.

Haxe usa la inferencia de tipos a pesar de que es seguro para los tipos bajo el capó. Personalmente, me gusta el hecho de que no tengo que definir el tipo de cada variable, pero puedes hacerlo si lo necesitas. De esa manera, tiene lo mejor de ambos mundos: no necesita especificar tipos (siempre que el compilador pueda descifrarlos) y escribir métodos "genéricos"/"plantilla" da como resultado una sintaxis mucho más limpia que en lenguajes tipificados estáticamente como C++ o C#, pero efectivamente el código es de tipo seguro y puede especificar el tipo solicitado cuando quiera usarlo para que el código sea más legible.

En Haxe se hace usando la sintaxis
var <name> [: <Type>] [=<initial value>]
que se ve muy raro viniendo de lenguajes tipeados pero tiene perfecto sentido viniendo de un lenguaje como GDScript.

Pero no tengo idea de cómo afecta esto a la complejidad y efectividad de la compilación JIT, solo quería señalar que esto sería una especie de término medio entre la tipificación dinámica como es ahora y la tipificación estática completa como lo es en lenguajes como C++.

Usé Haxe durante 1 ~ 2 años hace mucho tiempo.
Me gusta poder definir el tipo opcionalmente para el retorno de variables y funciones como dijo @Warlaan .

Preferiría el estilo de declaración de tipo c ++ antes que el mecanografiado/haxe:

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

O tal vez podríamos reflejar el estilo de export :

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

@ bojidar-bg Personalmente, prefiero más el estilo c ++ (mucho menos tipeo).

Hay muchos problemas que pueden surgir al poner el tipo antes de la variable. Go fue escrito por algunos de los implementadores de C más experimentados (incluido Ken Thompson) y usaron la notación Postfix (como Pascal y Haxe).

Creo que esta discusión pertenece a una discusión separada. No propongo una votación porque, por supuesto, la mayoría de las personas están acostumbradas a escribir lenguajes como C ++, C # y Java y podrían votar la sintaxis familiar, pero simplemente para recopilar materiales de investigación y opiniones para encontrar la solución que mejor se adapte a un lenguaje como GDScript. (que claramente no es como C en ningún otro aspecto).

Estoy de acuerdo en los cuatro aspectos:

  1. La sintaxis de C++ es más agradable de leer, pero
  2. en mi humilde opinión, la notación de sufijo tiene más sentido aquí
  3. que preferiría discutir (no decidir por mayoría)
  4. en otro hilo Lo siento, no quise secuestrar el hilo cuando mencioné el tema. Volvamos al tema de la compilación JIT.

¿La implementación de JIT a través de LLVM significaría que se preferiría Clang a GCC? ¿Es posible compilar LLVM JIT usando GCC?

A diferencia de Java, Python, C#, etc., GD Script funciona como parte de un motor de código nativo C++ más grande. De hecho, tanto GD Script como el resto del motor son una unidad. Algunos componentes del juego son C++ y otros son GD Script. En general, el rendimiento del juego depende de la creación de un equilibrio entre los dos. Escribir un juego de Godot únicamente en script lo haría más lento, pero escribirlo principalmente a través de nodos con un mínimo de gd script lo haría más rápido.

Entonces mi pregunta es. ¿Realmente vale la pena este pequeño aumento en el rendimiento?

Creo que la adición de C# de hecho puede ser mejor para el motor que implementar JIT en GD Script. C# es probablemente un lenguaje mucho más optimizado y eficiente, por lo que probablemente sea tan rápido como GD Script + JIT pero no tan integrado como GD Script.

Para responder a mi propia pregunta, personalmente no veo que este sea un tema importante en este momento. Tal vez en el futuro, cuando Godot tenga todas las funciones deseadas y los desarrolladores simplemente optimicen esas funciones.

¿La implementación de JIT a través de LLVM significaría que se preferiría Clang a GCC? ¿Es posible compilar LLVM JIT usando GCC?

Sí, pero entonces LLVM se convertiría en una dependencia, por lo que tendría más sentido usar LLVM para todo.

No creo que el aumento en el rendimiento sea pequeño para juegos más grandes. Tal vez para los juegos de arcade no se notaría, pero en los juegos con muchos bucles y llamadas a funciones definitivamente lo haría.

Además, ¿hay alguna discusión sobre por qué C# sobre Java o incluso C++? Creo que C# es un lenguaje muy bueno, pero el CLR y las herramientas son definitivamente inferiores a la JVM (especialmente en otras plataformas que no sean Windows).

@ paper-pauper, debe hacer una publicación en el foro sobre Java, JIT para continuar esta discusión. De hecho, preferiría Java> C # tbh. Creo que C# no fue una elección, sino más bien una oportunidad que los desarrolladores decidieron aprovechar.

para que al final nos decidiéramos? será JIT o no?

¿Qué hay de la capacidad de compilar GDScript en código C++ estático? ¿Sería eso posible?

@trollworkout : entonces probablemente sería más simple y más optimizado escribir la lógica de tu juego directamente en C++, ¿no?

@SuperUserNameMan No necesariamente. Primero, usaría C ++, no GDScript y, segundo, aún necesita encontrar una manera de cargar un objeto binario como un dll y conectarlo al motor sin tener que volver a compilar el motor con sus cambios de C ++.

@trollworkout Gracias a # 3936, podría ser posible compilar GDScript en C y cargarlo dinámicamente, eventualmente.

no es necesario traducir el script en c++. Derechos de @SuperUserNameMan

o necesita JIT o c# y todo será feliz

nah, el otro tema vinculado a @paper-pauper es en realidad mucho más interesante. La idea es usar C binario como un tipo de lenguaje de secuencias de comandos y llamar al código directamente a través de la reflexión en lugar de ser interpretado, haciéndolo tan rápido como el código nativo de C++. Creo que eso resolvería el problema de ABI. De hecho, con esto ya no necesitas ABI.

Actualmente estoy experimentando con la implementación de un GDScript JIT usando RPython. Los primeros resultados (muy preliminares) son prometedores, pero no sé lo difícil que será integrarlo en el motor.

¿Todavía hay interés en un GDScript JITed o la gente está más interesada en C # o GDScript estático?

Teniendo en cuenta que en realidad estás probando una posible solución, te sugiero que lo hagas y veas si hay suficiente aumento de rendimiento en los juegos para que valga la pena.

Lo que quiero decir con eso es que tal cosa solo valdría la pena si hay una ganancia de rendimiento bastante grande para la lógica compleja (o de lo contrario, tales ganancias podrían obtenerse mejor simplemente haciendo un trabajo de optimización general en el lenguaje en sí).

El rendimiento es crucial para mí, simplemente no puedo elegir a Godot para el guión.
Rendimiento. Pero creo que un lenguaje más rápido (Lua, Java, c#) es mejor que Jit, ya que Jit no funciona en iOS. Y al compilar en c++ se pierde la posibilidad de actualizar la lógica del juego en iOS

¿No es Lua más rápido solo por JIT? ¿O también tiene algún tipo de compilación AOT?

Todavía prefiero un GDScript escrito estáticamente (tal vez un sistema de escritura opcional, como TypeScript es para JavaScript). También tener la transpilación GDScript -> C++ resolvería muchos problemas de rendimiento, pero puedo ver que esto está lejos de ser trivial.

¿Cómo se actualiza la lógica del juego en iOS? no puedes descargar guiones de
tus juegos y ejecútalos en ios, todo tiene que ser instalado desde tu
ipa original que se publica en la tienda de aplicaciones. De cualquier manera tienes que
actualice la aplicación para obtener una nueva lógica, evento si se trata de nuevos scripts o un nuevo tiempo de ejecución
con la lógica compilada.

El 20 de agosto de 2016 a las 05:05, George Marques [email protected] escribió:

¿No es Lua más rápido solo por JIT? O tiene algún tipo de AOT
recopilacion tambien?

Todavía prefiero un GDScript tipado estáticamente (tal vez un tipeo opcional
sistema, como TypeScript es para JavaScript). También teniendo GDScript -> C++
la transpilación resolvería muchos problemas de rendimiento, pero puedo ver esto
está lejos de ser trivial.


Estás recibiendo esto porque estás suscrito a este hilo.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-241175124 ,
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/AGVmPZDOcIkO1-6ayDySyPnCfJdRk5Saks5qhm76gaJpZM4IuWZe
.

GDScript tipificado estáticamente con VM optimizada también sería bueno, ¿hay algún plan para esto?

El sistema de escritura opcional para GDScript como sugirió @vnen sería perfecto :)

Mientras investigaba sobre esto, encontré el gran SLJIT. He estado tratando de averiguar cómo podría integrarlo para permitir la compilación JIT al menos en el nivel de función de GDScript.

Después de algunas horas de leer/pensar, llegué a la conclusión de que sería de poco beneficio porque el mayor cuello de botella, ahora que he estudiado el código profundamente, es probablemente todo el material de Variant .

Terminar mi tarea para un examen sería excesivo, así que lo abandoné. De todos modos, una rama con mi trabajo inicial está en mi repositorio en caso de que alguien desee jugar con SLJIT y ya lo tenga integrado en el sistema de compilación.

Así que me sumo al grupo que cree que la escritura estática eliminaría muchas llamadas y bifurcaciones, lo que mejoraría notablemente el rendimiento.

@RandomShaper

Estoy de acuerdo en que el JIT no es factible actualmente, pero el problema es Variant y no la tipificación dinámica.

Experimenté escribiendo GDScript JIT usando RPython, que es la base para PyPy y está diseñado específicamente para crear JIT para lenguajes dinámicos.

No estoy de acuerdo con que el problema sea el tipeo estático, sería posible tener un GDScript JIT rápido si no tuviéramos que trabajar con Variant . Variant replica parte de la funcionalidad de los lenguajes dinámicos y, desafortunadamente, esa es la parte donde el JIT de RPython obtiene su impulso de velocidad. RPython necesita saber qué tipos tiene actualmente y luego puede acelerar esas partes.

Sin embargo, Variant está tan profundamente arraigado en Godot que es muy difícil prescindir de él. Básicamente, tendría que volver a implementar todos los tipos de datos de GDScripts y luego transformarlos en variantes.

Si alguien tiene una idea de cómo podemos arreglárnoslas sin usar Variants, sería posible un GDScript rápido (una prueba de concepto GDScript JIT mía que no usó Variant fue prometedora. Un bucle (tonto) que en GDscript tomó 20 segundos fue hecho en menos de un segundo cuando JITted)

Bueno, estaba tratando la escritura variante y dinámica como si ambas fueran lo mismo
cosa o como si uno no pudiera estar sin el otro.

Pero probablemente me estoy perdiendo algo sobre el diseño del lenguaje.

AIUI Variant hace posible usar C++ en Godot como si fuera un lenguaje de escritura dinámica. Especialmente en el tipo de retorno para algunos métodos. No estoy tan familiarizado con la fuente, pero no estoy seguro de si hay una forma limpia de evitarlo.

@brakhane ¿Quiere compartir su experimento con RPython? Creo que a todos aquí les vendría bien echarle un vistazo :smile:

Sin embargo, la variante está tan profundamente arraigada en Godot que es muy difícil prescindir de ella. Básicamente, tendría que volver a implementar todos los tipos de datos de GDScripts y luego transformarlos en variantes.

En teoría, ¿cuál sería el inconveniente de esto (aparte del tiempo necesario para implementarlo)? Este tipo de casting no es tan caro, según tengo entendido.

@paper-pauper mira a PyPy y RPython: Python está estrictamente tipado dinámicamente, y PyPy puede acelerar el código de Python muy bien pensado ... RPython es muy bueno para el desarrollo de intérpretes (con JIT casi gratis)

Se ha publicado un

La parte clave aquí es que Ruby elige implementar su JIT generando código C y llamando a un compilador C externo para construir un archivo .so luego cargarlo. No hace falta decir que esto ha creado muchos interrogantes y este artículo comienza a dar algunas respuestas.

Básicamente, las ventajas de usar un compilador externo (en lugar de integrar un marco JIT) son:

  • independencia a un solo compilador (puede usar GCC o LLVM dependiendo de lo que esté disponible/mejor)
  • Los marcos JIT como libGCCJit son jóvenes, por lo que su API puede ser mucho más frágil que C

Por supuesto, esto debe tomarse con muchas precauciones (los puntos de referencia del artículo comparan un prototipo con proyectos listos para producción como JRuby), pero muestra que, siempre que use optimizaciones inteligentes como encabezados precompilados, usar un compilador externo puede ser un solución viable.

Desde el punto de vista de Godot, esta técnica podría ser útil para proporcionar también una compilación estática de GDscript.

Estoy trabajando en la compilación JIT para mi tesis de maestría y planeo implementar algo adecuado para Godot. Hasta ahora, en su mayoría solo he leído muchos artículos sobre el tema, específicamente sobre cómo lidiar con la naturaleza de escritura dinámica de GDScript. Ahora comencé a trabajar en un prototipo y espero tener algo que mostrar relativamente pronto.

@DoctorAlpaca Casi he terminado con las comprobaciones de tipo estático para GDScript, creo que esto podría ayudarlo al menos un poco (puede buscar mi bifurcación si está interesado, y también # 10630). Estaba planeando investigar JIT eventualmente, pero no tengo experiencia con eso.

Hace algún tiempo comencé un experimento sobre JIT-ting GDScript. Configuré alguna fundación para ello.
En caso de que quieras echar un vistazo... https://github.com/RandomShaper/godot/tree/exp-gdscript-jit-master

@vnen De hecho, estoy muy emocionado de ver cuánta diferencia en el rendimiento puede traer la escritura estática, en comparación con el perfilado de tipo que estoy planeando hacer. Dado que también tendré que producir un texto real para mi maestría, no creo que haga mucho más que un prototipo, en el que habrá mucho margen de mejora.

@RandomShaper Esa configuración se ve bien, y después de ver las opciones disponibles, probablemente también opte por SLJIT. ¡Gracias por eso!

Llegaron los resultados de la encuesta de mayo de 2018 , y esta propuesta parecía estar en la prioridad de la hoja de ruta.

Personalmente, no creo que JIT GDScript sea una buena idea:

  • JIT no está permitido en plataformas móviles (al menos en IOS, no estoy seguro de Android), que son las que más necesitan este tipo de optimización...
  • JIT es un gran lío. Hay múltiples enfoques (rastreo vs método JIT), algunos de ellos ni siquiera deterministas :'-(
  • JIT es un GRAN lío GRANDE. La depuración es compleja (involucra volcados de memoria y código jited...), al igual que la creación de perfiles, al igual que el mantenimiento del código :'-(
  • JIT es un tipo especial de desorden. Los programadores JIT son un recurso escaso, por lo que mantener tal cosa sería complicado. Esto podría llevar a que hacer que GDScript evolucione sea cada vez más difícil, ya que el intérprete y el JIT estarían estrechamente acoplados.

El proyecto luaJIT ilustra bien esos puntos: el proyecto es simplemente brillante (el código base es realmente bueno, el rendimiento es increíblemente bueno), resuelve un caso de uso real, es utilizado por empresas comerciales.
Y, sin embargo, está muriendo lentamente ya que su autor principal ha retrocedido, incapaz de mantenerse al día con la nueva versión de lua (luaJIT se detuvo en 5.1, argumentando que agregar un número entero sobre un valor flotante en el lenguaje creado por el 5.2 sería realmente complejo para add), incluso si la comunidad lua tiene un tamaño mediano y luaJIT es el proyecto más conocido.

Obviamente, GDScript es un orden de magnitud más simple que Python o incluso lua, pero la carga de hacer un JIT sigue siendo importante, especialmente considerando que no es una característica central del proyecto Godot.

La única solución aceptable aquí sería confiar en llvm, que se encargaría de todas las partes locas y nos permitiría generar el código llvm-ir. Sin embargo, como se dijo antes, llvm es una gran dependencia (en este punto, casi podríamos enviar v8 con Godot y escribir un transpiler de GDScript a Javascript en su lugar :trollface: ), por lo que se requiere investigación adicional para comprender cuánto se agregaría aquí antes de continuar. en cualquier lugar. Y nuevamente, esta no sería una solución viable al menos para la plataforma IOS (¡maldita sea, Apple!).

Lamento sonar tan pesimista aquí, pero no publicaría esto sin una propuesta ;-)
Supongo que lo que los votantes de la encuesta entienden por "JIT GDScript" es "GDScript, pero rápido como código nativo". Entonces, al final, realmente no importa lo que signifique alcanzar este objetivo correctamente (AOT, JIT, transpilación + compilación, etc.)?

Por lo que vi de GDScript, una característica realmente genial (falta de) es que no es un lenguaje dinámico. No hay palabra clave exec , no hay parches mono, no puede modificar la variable durante la depuración, etc.
Esto hace que el lenguaje sea adecuado para la compilación estática (y, de hecho, es una razón más para no optar por JIT). Además de eso, el nuevo sistema de tipeo estático debería permitir una fácil optimización (en comparación con tener que hacer inferencia de tipeo).

Entonces, mi idea sería comenzar haciendo un transpiler GDScript-to-C simple (sin optimizaciones, solo use Variant en todas partes), que estaría disponible como godot --gdcc <myfile.gd> . Usar el compilador GDScript existente y la API GDNative debería hacer que esto sea relativamente fácil.
Luego, los usuarios deben ocuparse del resto de la cadena de herramientas (por ejemplo, compilar cada archivo C como .so y reemplazar el archivo .gd original por un archivo .gdns que apunta al .so).

En una segunda oportunidad, podríamos comenzar a preguntarnos acerca de las optimizaciones y, lo que es más importante, una forma de enviar un compilador a Godot.
Teniendo en cuenta este segundo punto, tinycc parece una solución realmente interesante dado su bajo peso, la cantidad de plataformas compatibles (x86, amd64, arm, arm64 para linux, win32 y osx), libtcc.h api que permiten integrarlo en un programa y cruza el modo de compilación. Además de eso, la versión 0.9.27 se lanzó en diciembre pasado (por lo que el proyecto parece estar vivo ^^).

Finalmente, de la misma manera que actualmente convertimos los archivos .gd en .gdc o .gde en el momento de la exportación, podríamos crear un nuevo tipo (¿.gdso?) automáticamente).

Estoy de acuerdo en que sería una exageración escribir un compilador JIT para GDScript. Como mencionas, la compilación JIT no está permitida en iOS (Android sí lo permite), por lo que tendrías que recurrir a la interpretación lenta en esa plataforma o escribir un compilador AOT (¡mira! Ahora tienes que mantener dos compiladores además de el interprete).

Dije muchas veces que considero que el transpilador GDScript2C es la mejor opción ahora que tenemos GDNative.

Para los archivos binarios creados con el módulo mono, podría escribir un compilador GDScript2IL y luego tendría la compilación JIT y AOT.

Por lo que vi de GDScript, una característica realmente genial (falta de) es que no es un lenguaje dinámico. No hay palabra clave exec , no hay parches mono, no puede modificar la variable durante la depuración, etc.
Esto hace que el lenguaje sea adecuado para la compilación estática (y, de hecho, es una razón más para no optar por JIT).

En realidad, en GDScript puede modificar el código fuente de un script en tiempo de ejecución.

En realidad, en GDScript puede modificar el código fuente de un script en tiempo de ejecución.

@neikeq Busqué esto antes de escribir mi publicación, supongo que no miré lo suficientemente cerca ^^
¿Puedes señalarme un ejemplo? (o donde se implementa en el código base)

En realidad, en GDScript puede modificar el código fuente de un script en tiempo de ejecución.

No tanto como "modificar". Puede configurar la propiedad source_code de Script con un código completamente nuevo y luego llamar a reload() para volver a compilarlo. Técnicamente, esto también se implementa para C#, ya que es parte de la API de script.

@touilleMan su idea es similar a lo que se discute en #11068.

El problema con transpilar GDScript, o compilarlo antes de tiempo, es que incluso con el sistema de tipo estático, todas las llamadas a métodos aún tienen que ser virtuales. Cualquier cosa podría esconderse detrás de un parámetro Node . Por lo tanto, se encontraría con problemas de rendimiento similares a los de Java, que usa JIT en su código de bytes (en parte) por las mismas razones.

En cualquier caso, como necesito una parte práctica para mi tesis de maestría, voy a implementar un JIT para GDScript de todos modos, así que después de eso podemos ver qué tan bien le va y cuánto destruye el código base. Sinceramente, no me importaría incluso si sigue siendo un prototipo para siempre y nunca se fusiona.

Además, para las cosas dinámicas, puede cargar archivos de script desde cualquier lugar del disco y usarlos para cualquier cosa para la que pueda usar scripts en su proyecto. Lo cual es una característica muy buena para DLC y/o contenido creado por el usuario (aunque no sé si alguien realmente lo está usando).

@vnen El código fuente en C# solo se usa para secuencias de comandos. No está compilado en reload .

@touilleMan

Teniendo en cuenta este segundo punto, tinycc parece una solución realmente interesante dado su bajo peso, su número de plataformas compatibles (x86, amd64, arm, arm64 para linux, win32 y osx), su api libtcc.h que permite integrarlo en un programa y cruza el modo de compilación. Además de eso, la versión 0.9.27 se lanzó en diciembre pasado (por lo que el proyecto parece estar vivo ^^).

¡Mala idea, es más probable que el proyecto esté muerto que vivo!

@neikeq

Para los archivos binarios creados con el módulo mono, podría escribir un compilador GDScript2IL y luego tendría la compilación JIT y AOT.

Pero esta es una opción interesante. Y quizás comparable a los costos de mano de obra con la adición de JIT.

En general, si ya hay una persona que se dedicará a esto en cualquier caso, déjelo que lo haga. El equipo no pierde con esto. Propongo relajarme y esperar, de repente algo saldrá (aunque no cuento con ello)

@DoctorAlpaca considerando el envío del método virtual, supongo que las cosas ya son así cuando se usa la API de C++. Entonces, ¿su objetivo aquí es hacer que GDScript sea más rápido que C++? ;-)
De todos modos, lo siento si mi publicación anterior te había aparecido en un estilo "es inútil, no deberías hacerlo".
La experiencia es inmensamente más valiosa que las suposiciones, por lo que solo puedo apoyarlo, además de eso, hay grandes posibilidades de que su trabajo lo lleve a mejorar varias partes del motor (corrección de errores, limpieza o incluso encontrar optimizaciones para el también) Dynamic-to-be-fast Variant) para que nunca se pierda nada.
Por cierto, si estás realmente interesado en JIT, te sugiero que vengas al Europython (finales de julio en Edimburgo, estaré allí ^^), el equipo de Pypy está allí todos los años y puedes hacer sprint coding en Pypy con ellos. durante todo el fin de semana. Por lo general, no hay mucha gente y son muy amables y pedagógicos, por lo que es básicamente como tomar una clase de 2 días en JIT con expertos de clase mundial ;-)

@ ret80 Estoy de acuerdo en que el proyecto tinycc no está realmente activo. Por otro lado, estamos hablando de un compilador C, el estándar más utilizado sigue siendo C99 (con C89 todavía muy presente), por lo que no hay muchas evoluciones que hacer una vez que el proyecto es lo suficientemente estable (supongo que podríamos decir que se podrían hacer evoluciones en la optimización, pero esto probablemente aumentaría el tamaño y la velocidad del compilador, por lo que no es el objetivo de este proyecto).
Mi mayor preocupación para este compilador es más el soporte de arquitecturas arm y la compilación cruzada .dll y .dynlib porque son características más exóticas (por lo tanto, potencialmente menos probadas).
Finalmente, dado que usaríamos este compilador con código autogenerado (siempre las mismas construcciones y gdnative como dependencia única), deberíamos estar relativamente seguros de no encontrar errores ocultos una vez que el proyecto esté en marcha.

@neikeq ¡ Me parece muy elegante la idea de usar el tiempo de ejecución mono para el JIT! Esto mantendría bajo el número de dependencias (y mejoraría el número de personas involucradas en el módulo mono que siempre es mejor) y nos evitaría hacer otro lanzamiento.

Honestamente, no compilaría JIT sino AOT. Algo como TerraLang (una interfaz lua para LLVM diseñada para construir lenguajes dinámicamente) sería útil tanto para el desarrollo de la recarga en caliente de la fuente como para la compilación en objetos nativos binarios para la vinculación de la compilación (sin interpretación, LLVM puro compilado) sería útil como sería bastante trivial especializarse incluso para tipos de nodos específicos. Es probable que la sobrecarga virtual siga siendo un problema, pero sería mucho más rápido de lo que es ahora, especialmente una vez que la información de escritura comience a existir. Personalmente, incrustaría LLVM en el editor/compilador, sí, aumentaría el tamaño, pero también significa que los juegos en sí mismos podrían terminar siendo aún más pequeños, especialmente si incluye los archivos de objetos y/o la fuente de godot con el editor y deja LTO todo en una salida final más pequeña y más rápida.

El proyecto luaJIT ilustra bien esos puntos: el proyecto es simplemente brillante (el código base es realmente bueno, el rendimiento es increíblemente bueno), resuelve un caso de uso real, es utilizado por empresas comerciales.
Y, sin embargo, está muriendo lentamente ya que su autor principal ha retrocedido, incapaz de mantenerse al día con la nueva versión de lua (luaJIT se detuvo en 5.1, argumentando que agregar un número entero sobre un valor flotante en el lenguaje creado por el 5.2 sería realmente complejo para add), incluso si la comunidad lua tiene un tamaño mediano y luaJIT es el proyecto más conocido.

No estoy seguro de si lo llamaría muerto, se bifurcó en github hace mucho tiempo y se está actualizando a versiones modernas de LUA, etc.

¡Encuentro realmente elegante la idea de usar el tiempo de ejecución mono para el JIT! Esto mantendría bajo el número de dependencias (y mejoraría el número de personas involucradas en el módulo mono que siempre es mejor) y nos evitaría hacer otro lanzamiento.

Sin embargo, todavía no está permitido en iOS, etc.

Todavía creo que la idea JIT debería fusionarse con GDNative de alguna manera para que GD Script se precompile como una especie de código de operación C de terceros y GDNative debería poder analizar este código de operación y traducirlo a código C ++.

@OvermindDL1 muchas gracias por mencionar TerraLang, parece un proyecto muy interesante ;-)

No estoy seguro de si lo llamaría muerto, se bifurcó en github hace mucho tiempo y se está actualizando a versiones modernas de LUA, etc.

No estoy muy cerca de la comunidad lua, por lo que mi comprensión puede estar equivocada sobre este tema. ¿Puede señalarme la versión bifurcada de luaJIT que implementa las nuevas versiones de lua?

@OvermindDL1 muchas gracias por mencionar TerraLang, parece un proyecto muy interesante ;-)

Son muy amigables con las relaciones públicas, es un proyecto realmente bueno que muchos aún no han notado. Es bastante agradable crear un lenguaje interno, analizadores integrados, integración LLVM, puede generar binarios completamente compilados y optimizados, etc.

Incluso considerando eso, si tuviera que escribir un compilador back-end nativo para GDScript, probablemente solo usaría la última versión de la API C++ de LLVM. La API de C es estable, pero la API de C++ tiene mucha más potencia, mucha potencia que se necesita para implementar un lenguaje y no solo escanear, especialmente si desea usar libclang para el paquete de optimización completo. Sin embargo, TerraLang también es completamente funcional de esa manera y es significativamente más fácil de usar si se prueba menos y se usa una versión anterior de LLVM (API de C ++, aunque quieren actualizar, así que, por supuesto, PR es bienvenido).

No estoy muy cerca de la comunidad lua, por lo que mi comprensión puede estar equivocada sobre este tema. ¿Puede señalarme la versión bifurcada de luaJIT que implementa las nuevas versiones de lua?

Me encontré con un par de interesantes en https://github.com/LuaJIT/LuaJIT/network en un momento, se ha bifurcado muchas veces y hay mucho trabajo de desarrollo en algunas bifurcaciones grandes. Tampoco soy muy grande en la comunidad de Lua, implemento luajit en algunos proyectos para obtener soporte de secuencias de comandos, pero realmente no escribo lua yo mismo (aparentemente, lua tiene un administrador de paquetes hoy en día, luarocks o algo así). Me encontré con una gran bifurcación o dos de luajit que no está directamente bifurcada del repositorio de luajit también (por lo que no aparecerá en esa lista), y 'creo' que la más grande que vi no estaba en esa lista. .

Admito que perdí un poco la pista de todas las cosas que la gente propone aquí, pero parece que al menos hay algunos que dicen que GDScript debe escribirse estáticamente o que el JIT debe basarse en LLVM. Estoy totalmente en desacuerdo con estas afirmaciones.

Recientemente comencé a trabajar en JIT para GDScript usando RPython (el conjunto de herramientas que PyPy usa para crear su JIT), y también he considerado usar LLVM como una alternativa.

Para abreviar una larga historia: LLVM no es adecuado para JIT, he leído bastantes publicaciones de blog que llegan a esa conclusión, y mis experimentos con él también parecen confirmarlo. Hay dos razones principales:

  1. LLVM es lento. No me refiero al código generado por LLVM, me refiero al tiempo que le toma a LLVM crear ese código que crea un cuello de botella para un JIT. JIT de Webkit cambió de LLVM a una solución casera, y esa fue una de las razones

  2. LLVM asume que su código está tipificado estáticamente y no es adecuado para compilar lenguajes tipificados dinámicamente.

Es por eso que dejé LLVM (lo cual es una pena, porque me gusta mucho) y estoy trabajando con RPython. Tiene pros y contras:

Los profesionales

  1. RPython encaja perfectamente: es un sistema para crear intérpretes JIT para lenguajes de escritura dinámica. Junto con Python (PyPy), la gente ha escrito intérpretes de PHP que resultaron ser mucho más rápidos que el oficial, Ruby y lenguajes personalizados.

  2. RPython es un excelente sistema para crear JIT de rastreo para lenguajes de escritura dinámica. Para lenguajes tipeados dinámicamente, un JIT de seguimiento parece ser la mejor solución. La implementación de un JIT de seguimiento es un trabajo duro y requiere agregar puntos de medición al código sin JIT. RPython ahora existe desde hace más de 10 años, tiene personas muy inteligentes trabajando en él (y se han publicado algunos artículos académicos sobre RPython), y requiere que haga muy poco para tener un JIT en funcionamiento. Básicamente, espera que escriba un intérprete de código de bytes en RPython y le diga qué códigos de bytes son instrucciones de salto y hace el resto (y también lo hace bastante bien; los primeros experimentos sugieren que podría ser posible una aceleración de 40x para código pesado de cálculo)

  3. El código JITted es bastante rápido y admite backends x86, x64 y ARM

  4. Es un proyecto activo con bastantes personas trabajando en él y una comunidad activa.

  5. El intérprete de bytecode y las funciones son bastante legibles. La implementación actual en Variant_op.cpp se ve horrible; no me malinterpreten, sé por qué se ve así y que es necesario para un buen rendimiento, pero hackearlo no es muy divertido.

Los contras

  1. (Partes de) Variant.cpp deben reescribirse/duplicarse en RPython.

La solución de escritura dinámica de GDScript, la clase Variant, dificulta la creación de un buen código JITTed. Una de las cosas que hace la canalización JIT es notar cuándo se suele llamar a una función con un parámetro int, por ejemplo, y luego se optimiza para este caso. Si solo usáramos Variant como tipos opacos, las pruebas sugieren que solo podemos obtener una mejora de velocidad 2x, porque todo lo que terminamos de manera efectiva es compilar GDScript en código C ++ que efectivamente llama a Variant para cada operación, como sumar o restar.

Por lo tanto, para obtener un uso real del JIT, al menos algunas partes de Variant deben duplicarse como código RPython para que el JIT sepa qué está pasando. No es muy bonito, pero parece funcionar.

  1. RPython crea código C, no C++. Un poco desafortunado, pero podemos hacer que funcione, especialmente con la interfaz GDNative

  2. Tenemos código escrito en dos lenguajes, C++ y RPython. Sin embargo, Godot ya usa SCons, que es Python, por lo que, estrictamente hablando, no estamos agregando un nuevo idioma.

  3. No es divertido programar en RPython. Básicamente, terminas escribiendo en un lenguaje que es un poco más poderoso que C, pero no tan poderoso como C++, y lleva un tiempo acostumbrarse a qué construcciones de Python puedes usar y cuáles no.

  4. El proceso de traducción para convertir el código RPython en C (que será el JIT completamente funcional) lleva mucho tiempo, y no exagero. En esta etapa inicial de desarrollo, "solo" toma de 1 a 2 minutos, pero espero que tome de 20 a 40 minutos para la implementación algo completa. Sin embargo, esto no es tan horrible como parece, porque el código C solo necesitaría generarse cuando el JIT se modifique, de lo contrario, el proceso de compilación de Godot solo puede usar los archivos C generados.

Espero tener un PoC funcionando en 4 a 8 semanas para recibir más comentarios.

@baekdahl Demasiada lectura. GDScript se escribirá sin importar lo que pienses. La decisión vino desde arriba y sucederá en un futuro cercano.

@CarlGustavAlbertDwarfsteinYung GDScript se está tipeando opcionalmente . Gran diferencia para este problema.

Para abreviar una larga historia: LLVM no es adecuado para JIT, he leído bastantes publicaciones de blog que llegan a esa conclusión, y mis experimentos con él también parecen confirmarlo. Hay dos razones principales:

Eso es porque el JIT debería estar en tiempo de desarrollo. Al realizar un lanzamiento, debe realizarse una compilación nativa estática completa. Es posible que LLVM no sea el JIT más rápido, pero es un JIT y, al mismo tiempo, puede compilar completamente de forma nativa en bibliotecas/objetos/programas independientes.

LLVM asume que su código está tipificado estáticamente y no es adecuado para compilar lenguajes tipificados dinámicamente.

Mmm, en realidad tiene algunas interfaces de escritura dinámica bastante fantásticas.

Independientemente, si GDScript se está tipeando, eso no es un problema de todos modos, las partes 'dinámicas' de GDScript se convertirían en una variante de todos modos.

Es por eso que dejé LLVM (lo cual es una pena, porque me gusta mucho) y estoy trabajando con RPython. Tiene pros y contras:

@brakhane ¿Es capaz de compilación nativa? Si no es así, parece que no es un iniciador, ya que no puede JIT en plataformas como iOS, además de que un JIT en tiempo de ejecución ralentizará la carga y la cola donde AOT no tiene ese costo en los lanzamientos.

GDScript se está tipeando opcionalmente. Gran diferencia para este problema.

Sí, pero incluso las partes 'sin tipo' son variantes, lo que es bastante trivial de representar en LLVM.

Eso es porque el JIT debería estar en tiempo de desarrollo.

Entonces no es JIT, sino una compilación anticipada normal.

¿Es capaz de compilación nativa? Si no es así, parece que no es un iniciador, ya que no puede JIT en plataformas como iOS, además de que un JIT en tiempo de ejecución ralentizará la carga y la cola donde AOT no tiene ese costo en los lanzamientos.

No, es un JIT. Y si iOS no permite JIT, no funcionará allí. 🤷

Sí, pero incluso las partes 'sin tipo' son variantes, lo que es bastante trivial de representar en LLVM.

Pero no obtienes tanta mejora de velocidad, como mencioné.

Es bastante fácil compilar GDScript así:

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

a algo equivalente al siguiente C++

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

(o algo así, no recuerdo la API variante exacta, ha pasado algún tiempo)

Pero esto solo te dará una mejora de 2 veces en la velocidad. Convertirlo en while (i<10) {foo.call(new Variant(i)));i += 1} es mucho más difícil de lograr.

Mmm, en realidad tiene algunas interfaces de escritura dinámica bastante fantásticas.

Me encantaría saber acerca de esos.

Entonces no es JIT, sino una compilación anticipada normal.

Es posible que un JIT no produzca un código tan rápido en general como AOT, pero es un tiempo de respuesta mucho más rápido durante el desarrollo, por lo que aún es útil allí. Sin embargo, sería bastante bueno con AOT incluso en el momento del desarrollo, ya que si los módulos individuales se compilan por separado, no hay mucho problema de velocidad independientemente (<1s fácil).

No, es un JIT. Y si iOS no permite JIT, no funcionará allí.

Ah, sí, eso no sería útil entonces. iOS requiere un intérprete lento completo o requiere AOT. Además, AOT carga más rápido, requiere un tiempo de ejecución menor, puede realizar LTO, etc.

Pero no obtienes tanta mejora de velocidad, como mencioné.

Excepto que Typed GDScript podrá inferir 'la mayoría' de los tipos independientemente (incluso podría decorar la variante en las llamadas get_node para crear una ruta feliz para el tipo de nodo esperado en función de la escena con el respaldo de la variante estándar ). Sin embargo, en la gran mayoría de los casos, incluso en el código sin tipo, aún podrá inferir la gran mayoría de los tipos, obteniendo así mejoras significativas en el rendimiento y una reducción del código generado.

(o algo así, no recuerdo la API variante exacta, ha pasado algún tiempo)

Eh, esa no es ni remotamente la forma en que sugeriría hacerlo honestamente. Realizar una operación nativa de < través de un visitante sería significativamente más eficaz que la prueba de cadenas como solo un ejemplo. Sin embargo, no estoy seguro de que este ejemplo sea muy válido, ya que me imagino que el tipo de i se inferiría como un número entero arriba de donde está configurado (código no mostrado) e incluso si no lo es, entonces podría ser una verificación de tipo único antes del ciclo para verificar si funciona con tales comparaciones integrales y si no arroja una excepción o error de alguna otra manera, de lo contrario, adquiera el valor integral / flotante fuera de la variante (probablemente convertido en algo útil para minimizar la generación de código, LLVM ya tiene pases para optimizar esto) y el bucle se optimizaría por completo independientemente. Desea minimizar el tiempo de variante por completo y verificar su acceso temprano y con frecuencia. Recurrir a los visitantes tampoco es tan lento, especialmente porque la mayoría de los visitantes arrojarían un error (como llamar a un método en un número entero).

Pero esto solo te dará una mejora de 2 veces en la velocidad. Convertirlo en while (i<10) {foo.call(new Variant(i)));i += 1} es mucho más difícil de lograr.

¿Sin embargo, esto parece bastante trivialmente optimizable? Todo sería bastante nativo hasta llamar a un trampolín para que foo despache apropiadamente más o menos (función de contenedor simple para realizar el envío sin tipo para escribir o error), pero incluso entonces si el tipo de foo Se conoce

Me encantaría saber acerca de esos.

Además del JIT de generación de código nativo en línea que tiene LLVM (que funcionaría perfectamente con todo lo anterior y sería fantástico para el tiempo de desarrollo, edite el código mientras se ejecuta mientras aún puede hacer AOT para las versiones) que permite una directa Traducción JIT, también hay bibliotecas como pyston, fastlua, entre otras construidas en LLVM que tienen varias formas de seguimiento de JIT para la mejora bajo demanda, que personalmente encuentro bastante inútil para el tiempo de desarrollo y no se puede usar en una versión confiable tiempo de todos modos debido a plataformas como iOS, así que no veo el punto.

@ OvermindDL1 Todavía está hablando de AOT que se activa automáticamente al guardar un GDScript editado.

Y sí, si GDScript obtiene escritura estática opcional, aquellas partes en las que se imponen los tipos se pueden convertir en un código de máquina bastante efectivo. GDScript también está bastante limitado con lo que puede hacer, por lo que no creo que incluso hacerlo estrictamente estático en todas partes no sea una gran pérdida.

Y si la gente quiere implementar tal cosa, puede hacerlo. Ya he escrito algunos compiladores estáticos y ese problema no me interesa.

Lo que es interesante para mí es crear un JIT para un GDScript de tipo dinámico que se ejecuta en PC. Sería bueno que otras personas terminaran usándolo también, pero no es un requisito para mí. Sin embargo, aprecio la advertencia de "puede que estés perdiendo el tiempo".

Eh, esa no es ni remotamente la forma en que sugeriría hacerlo honestamente. Realizar una operación nativa < a través de un visitante tendría un rendimiento significativamente mayor que la prueba de cadenas como solo un ejemplo.

IIRC, no es realmente un "<" sino más bien un parámetro Operation.LT que se proporciona.

¿Sin embargo, esto parece bastante trivialmente optimizable?

Sin embargo, no lo es, a menos que desee volver a implementar un intérprete de GDScript completamente nuevo. E incluso entonces, tendrá dificultades para inferir tipos, excepto en casos triviales.

Si simplemente desea reemplazar la parte del intérprete de código de operación, no es tan trivial (dato curioso: los archivos ".gdc" no son código de bytes, sino más bien como el código fuente tokenizado que se interpreta y se convierte en código de bytes en memoria más adelante). En este momento, usted sabe lo siguiente:

  • Hay una variable i, que se compara ("menor que") con otra variante cuyo contenido es opaco (aunque podría usar la API interna para ver que es un número entero con valor 10)
  • Hay otra variable "foo", que se llama con i como parámetro
  • se me asigna un nuevo valor, el resultado de agregar i a otra variante opaca

Si desea poder realizar la compilación AOT, básicamente debe volver a implementar todo el intérprete de GDScript. Lo cual no es tan trivial como parece, ya que una parte significativa del intérprete es parte de la clase Variant. Y Variant es lo que mantiene unido a Godot, incluso las partes que no son GDScript dependen en gran medida de Variant. Entonces, cuando comienza a jugar con él, de repente corre el riesgo de romper la compatibilidad con versiones anteriores de la interfaz GDNative.

Realmente no es tan fácil como parece al principio; También me sorprendió la primera vez que me sumergí en él.

Todavía está hablando de AOT que se activa automáticamente al guardar un GDScript editado.

No en todas partes, sino solo en Export. Ahora preferiría que fuera AOT al guardar, pero no tiene por qué serlo, podría simplemente AOT en Liberar/Exportar y JIT al ejecutarlo a través del editor para permitir el intercambio de código en caliente durante el tiempo de ejecución.

Y sí, si GDScript obtiene escritura estática opcional, aquellas partes en las que se imponen los tipos se pueden convertir en un código de máquina bastante efectivo. GDScript también está bastante limitado con lo que puede hacer, por lo que no creo que incluso hacerlo estrictamente estático en todas partes no sea una gran pérdida.

Según la especificación actual, hay muchas inferencias, 'la mayoría' del código debería, en general, escribirse automáticamente por inferencia. Estoy bastante emocionado por eso. (Soy fanático de escribir HM, ¡lo he implementado una docena de veces en una variedad de formas a lo largo de los años!)

Lo que es interesante para mí es crear un JIT para un GDScript de tipo dinámico que se ejecuta en PC. Sería bueno que otras personas terminaran usándolo también, pero no es un requisito para mí. Sin embargo, aprecio la advertencia de "puede que estés perdiendo el tiempo".

Sí, un JIT no debería ser obligatorio en absoluto, ya que eso significa que no sería exportable a todas las plataformas compatibles (además de agregar gastos generales). AOT o interpretación siempre debe ser el método principal, y teniendo en cuenta que la mayoría de las plataformas que no son compatibles con JIT (iOS, webassembly, etc.) también tienden a ser las plataformas menos eficientes, entonces AOT debería ser absolutamente el predeterminado. , no interpretación, lo que es aún mejor ya que se está agregando tipeo.

Sin embargo, no lo es, a menos que desee volver a implementar un intérprete de GDScript completamente nuevo. E incluso entonces, tendrá dificultades para inferir tipos, excepto en casos triviales.

No hablo de interpretación, hablo de compilación. Si se compila un código completamente no tipificable, se convertiría en variantes en el código de la máquina (tipo de suma etiquetada), y cualquier acceso a él, como digamos, una función operator< simplemente realizaría una llamada de visitante en eso, lo que implica un solo salto estático basado en el valor de tipo dinámico, luego la ejecución del código estático (que si es, digamos, un número entero está en línea, si es, digamos, un tipo personalizado, entonces probablemente terminaría realizando una llamada virtual, o incluso una llamada estática si es un tipo grande implementado directamente en la variante como un vector).

Si simplemente desea reemplazar la parte del intérprete de código de operación, no es tan trivial (dato curioso: los archivos ".gdc" no son código de bytes, sino más bien como el código fuente tokenizado que se interpreta y se convierte en código de bytes en memoria más adelante).

En absoluto, los códigos de operación no deberían existir en absoluto en el momento en que AOT esté completo, debería ser un código de máquina puro reducido al mayor de los despachos variantes en código de máquina o al más pequeño de código de máquina eficiente perfectamente conocido y tipeado.

Hay una variable i, que se compara ("menor que") con otra variante cuyo contenido es opaco (aunque podría usar la API interna para ver que es un número entero con valor 10)

Si se conoce el tipo de la variable i , puede generar un código de máquina que pruebe el tipo de la variante opaca y, si no es una coincidencia compatible inmediata, entonces se produce un error inmediatamente; de ​​lo contrario, realice la comparación en función de i tipo. Si no se conoce el tipo de la variable i , será un envío completo de visitantes en las dos variantes (una llamada de función conocida estáticamente) para adquirir las etiquetas de tipo de las variantes y saltar al código de máquina apropiado para los tipos dados.

Hay otra variable "foo", que se llama con i como parámetro

Si se conoce el tipo de llamada de foo , como un ejemplo en la notación ML, int -> int , entonces si también se conoce el tipo de i y también es un int entonces realice eficientemente código de máquina, si no se conoce el tipo de i , pruebe la etiqueta de tipo si coincide, si no es así, entonces error, si es así, extraiga el valor y realice un código de máquina eficiente. Si no se conoce el tipo de llamada de foo pero la etiqueta de tipo indica que se puede llamar (de lo contrario, error), entonces si se conoce el tipo de i (para todas las definiciones de known aquí me refiero a que ambos se conocen en tiempo de compilación y, por lo tanto, no están empaquetados), luego envuélvalos en una variante (configuración en línea eficiente de la etiqueta de tipo adecuada y colocando los bits de i en los bits correctos) y realice una llamada de variante a la función de trampolín envuelta, que luego verifica los argumentos y llama a la función interna real (que se habría llamado directamente si el tipo de llamada de foo fuera conocido estáticamente).

se me asigna un nuevo valor, el resultado de agregar i a otra variante opaca

Todo se basa en cuál es ahora el tipo estático conocido de i , y realiza la generación de código nativo apropiada descrita anteriormente según sea necesario.

Si desea poder realizar la compilación AOT, básicamente debe volver a implementar todo el intérprete de GDScript.

Lo cual es aún menos trabajo que crear un JIT específicamente para GDScript a menos que esté envolviendo un JIT existente (cuyas operaciones esperadas pueden no coincidir perfectamente con GDScript). La generación de AOT en sí misma no es mucho más difícil que crear un intérprete, especialmente en LLVM, ya que hace el trabajo mucho más duro por usted (optimizaciones, LTO, generación de código de máquina, depurador, etc... etc...).

Lo cual no es tan trivial como parece, ya que una parte significativa del intérprete es parte de la clase Variant.

Pero aún así no es tan difícil como implica (he escrito una gran variedad de lenguajes a lo largo de las décadas, muchos hasta el código de máquina, tanto directamente en x86 como a través de LLVM).

Y Variant es lo que mantiene unido a Godot, incluso las partes que no son GDScript dependen en gran medida de Variant. Entonces, cuando comienza a jugar con él, de repente corre el riesgo de romper la compatibilidad con versiones anteriores de la interfaz GDNative.

Precisamente por eso, las variantes deben integrarse directamente en el código de la máquina. Sin intérprete de código de operación ni nada por el estilo, sino que realiza un envío de visitantes completo y adecuado (ya sea a través de un salto local basado en la etiqueta de tipo o incluso a través de una llamada larga desde una matriz estática (tabla de envío)). Esto no solo mantendría la compatibilidad con versiones anteriores, sino que también permitiría que la interfaz GDNative luego cargue directamente las bibliotecas compiladas completamente AOT (o si Godot obtiene la capacidad de LTO del motor a las interfaces GDNative a través de objetos binarios, entonces se podrían aplicar optimizaciones LTO, lo que podría luego, reduzca significativamente el tamaño de salida del programa final, ya que el código no utilizado conocido podría eliminarse, aunque eso sería una tarea posterior).

Realmente no es tan fácil como parece al principio; También me sorprendió la primera vez que me sumergí en él.

Sin duda, es una cantidad de trabajo no trivial y, de hecho, la mayor parte es una gran cantidad de trabajo ocupado para traducir la canalización de compilación GDScript anterior para generar código ensamblador LLVM (aunque tiene una API fantástica para hacerlo) en lugar de códigos de operación. Pero es completamente factible, y en no tanto tiempo como cabría esperar dada la implementación del lenguaje.

Por cierto, todos los operadores Variant se tratan aquí: https://github.com/godotengine/godot/blob/d2b75557a5dedf951ee036ca01af4f94bc059069/core/variant_op.cpp#L391 -L392

Que ya está bastante optimizado.

Por cierto, todos los operadores Variant se tratan aquí:

Ah, genial, entonces ya está usando un goto de computadora, podría usar eso entonces (aunque probablemente también querría agregar eso a LLVM para que pueda incluir rutas de llamadas conocidas cuando sea posible, pero sí, exactamente algo como esto, esto es mucho un visitante variante y se usaría en el peor de los casos cuando no se conocen tipos en absoluto. Aunque si se sabe algo sobre los tipos, entonces sería aún más rápido hacer llamadas optimizadas más directamente y, por supuesto, si todos los tipos se conocen o solo hay una única rama válida, entonces se puede generar un código de máquina no variante optimizado (ya sea extrayendo los datos de la variante (más probablemente pasando en la sección de bits emitidos) o simplemente usando los datos que ya están en registros/pila/montón si es local o ya extraído).

Pero no obtienes tanta mejora de velocidad, como mencioné.

Es bastante fácil compilar GDScript así:

mientras i<10:
foo(yo)
yo = yo + 1

a algo equivalente al siguiente C++

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

(o algo así, no recuerdo la API variante exacta, ha pasado algún tiempo)

Pero esto solo te dará una mejora de 2 veces en la velocidad.

Entonces, ¿no haría que GDScript compilado sea más rápido que C # en la mayoría de los casos?
Un mejor rendimiento con un lenguaje más simple me parece un buen negocio.

Entonces, ¿no haría que GDScript compilado sea más rápido que C # en la mayoría de los casos?

Probablemente no, dado que tendría que pasar por toda la capa Variant de direccionamiento indirecto. Mirando lenguajes como Wren, parece que el uso de etiquetas NaN o tipos sin caja es más perjudicial para el rendimiento que compilar cosas en código de máquina. C# tiene ambos tipos sin empaquetar _y_ compilación JIT, por lo que compilar GDScript en código de máquina no lo hará mágicamente más rápido que C#.
Tenga en cuenta que GDScript ya tiene un despachador de código de bytes bastante optimizado, tener un código de máquina para hacer lo mismo no aceleraría el cuello de botella.

Bueno, tal vez una buena solución sería algo que genere el código C++ no optimizado desde el gdscript (quizás también el archivo sconscript) y luego el usuario lo optimizaría (no debería ser un gran problema ya que el usuario escribió el script original) y lo compilaría. manualmente

No es perfecto, pero ya es más rápido que hacerlo todo a mano.

Si va a escribir el código dos veces, ¿por qué no escribirlo directamente en C++?

Esa es otra cosa sobre traducir GDScript directamente a LLVM, puede AOT un binario para cualquier plataforma, no solo en la que se encuentra actualmente y compilables cruzados.

Este problema me recuerda al lenguaje Boo . Un lenguaje inspirado en Python que se ejecuta en .NET.

No sé lo que vale (la verdad es que no lo he usado), y el proyecto parece muerto (¿5 años? Maldición...) pero lo estoy lanzando de todos modos.

Prefiero la declaración de tipos de Kotlin. es mas legible

var name:String = "Bob"

@Logmytech Tiene medio año de retraso, Typed GDScript ya está en el maestro (y resulta que usa declaraciones de tipo Kotlin-ish).

Ruby tiene un nuevo compilador JIT: https://bugs.ruby-lang.org/projects/ruby/wiki/MJIT#MJIT -organization

Solo por curiosidad, ¿está planeado para 4.0?

@girng , existe la sospecha de que esto no sucederá en Godot 4.0. Pero no es exactamente :)

Creo que v4.0 podría tener una transpilación preliminar de GDScript a C, al igual que v3.0 tenía compatibilidad preliminar con C#.
(si tenemos suerte).

Es más fácil hacer soporte para el lenguaje ZIG . Lenguaje prometedor. Aunque me gusta mucho el GDScript, y si funcionara más rápido sería genial. Por cierto, sería genial si el archivo en sí no fuera una clase; la clase se escribía en el archivo, como se hace en otros lenguajes. Debido a esta característica, surgen algunas dificultades que podrían eludirse fácilmente.

Lo más probable es que Godot 4.0 no tenga JIT, pero tendrá mejoras de rendimiento en GDScript.

He realizado un trabajo significativo para compilar GDScript a C con GDNative. He llegado a un punto en el que no estoy convencido de que valga la pena completarlo. En teoría, el rendimiento del código AOT podría ser mucho más rápido que el de GDScript en ejecución, pero en la práctica los resultados son más decepcionantes. Los costos de pasar por la capa GDNative son significativos y, sin cambios importantes en GDScript, el código compilado es muy frágil (el código de bytes de GDScript no se genera de una manera segura para ejecutarse en una ejecución posterior porque espera que el entorno sea estático , incluidas las posiciones de las cosas en los arreglos internos del motor).

Sin embargo, estoy convencido de que al implementar una cantidad mínima de optimizaciones de back-end, se podrían lograr algunas mejoras de rendimiento reales.

Estoy trabajando en una bifurcación que incluye una serie de optimizaciones que no requieren ningún cambio significativo en GDScript, pero que ya genera algunas ganancias de rendimiento.

Hasta ahora, he implementado las estructuras de datos repetitivas para realizar la optimización del código, así como algunos pasos básicos de optimización:

  • construcción y destrucción del gráfico de flujo de control
  • análisis de flujo de datos que incluye gens y kill sets, rangos en vivo
  • eliminación de código muerto
  • saltar subprocesos (optimización importante) ya que GDScript crea muchas ramificaciones innecesarias

Como POC, planeo implementar algunos pases más antes de empujar mi tenedor:

  • eliminación de subexpresiones comunes
  • inferencia de tipo de temporales y aritmética nativa para temporales (tampoco estoy convencido de que la aritmética tipeada sea una gran victoria)

Las operaciones aritméticas con tipo requieren modificaciones en GDScriptFunction y las clases de estado de función para incluir matrices de registros con tipo, así como nuevos códigos de operación para abordar los registros con tipo y evitar la verificación de tipo adicional en tiempo de ejecución.

Puede ver mi progreso en las optimizaciones mencionadas anteriormente en mi bifurcación de Godot, https://github.com/pchasco/godot/tree/gdscript-optimization/modules/gdscript/optimizer

@pchasco Es posible que desee hablar con @vnen , que actualmente está reelaborando GDScript para Godot 4.0 - En mi opinión, este sería un buen momento para asegurarse de obtener una fruta tan madura :)

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

@pchasco Es posible que desee hablar con @vnen , que actualmente está reelaborando GDScript para Godot 4.0 - En mi opinión, este sería un buen momento para asegurarse de obtener una fruta tan madura :)

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

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

Bueno, la VM va a cambiar para integrar instrucciones escritas, así que no estoy seguro de si hacer el trabajo de optimización ahora es lo mejor. Creo que esas ideas se pueden usar en la nueva máquina virtual, pero llevará un tiempo hasta que se complete.

Sin embargo, no estoy seguro de si alguna vez tendremos JIT, por lo que tal vez este problema podría cerrarse a favor de otra cosa. Hay una idea de hacer una compilación AOT que sería más fácil de administrar que esto.

@vnen Sí, el título del problema ya no se ajusta, si lee lo anterior, encontraron que JIT no valía la pena en su mayoría y, en cambio, optaron por optimizaciones específicas en el intérprete actual.

Puede valer la pena tenerlos en cuenta mientras se reelabora eso, pero sí, sabrás mucho mejor que yo cuándo es un buen momento para ver eso :) solo quería conectarlos a los dos

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

@pchasco Es posible que desee hablar con @vnen , que actualmente está reelaborando GDScript para Godot 4.0 - En mi opinión, este sería un buen momento para asegurarse de obtener una fruta tan madura :)
Ver https://godotengine.org/article/gdscript-progress-report-writing-tokenizer

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

Veo que encontraste mi blog!

Bueno, la VM va a cambiar para integrar instrucciones escritas, así que no estoy seguro de si hacer el trabajo de optimización ahora es lo mejor. Creo que esas ideas se pueden usar en la nueva máquina virtual, pero llevará un tiempo hasta que se complete.

Sin embargo, no estoy seguro de si alguna vez tendremos JIT, por lo que tal vez este problema podría cerrarse a favor de otra cosa. Hay una idea de hacer una compilación AOT que sería más fácil de administrar que esto.

Estoy de acuerdo en que JIT no es una solución viable dada la cantidad de plataformas, el hardware variable y la cantidad de trabajo necesario para mantener. AOT es definitivamente la solución más simple y portátil para el rendimiento nativo con GDScript. De hecho, Unity también transpila .NET a C++ para su AOT. Me interesaría contribuir con la reescritura de GDScript.

@pchasco Sugeriría unirse al canal IRC #godotengine-devel y ponerse en contacto allí :)

Como mencioné antes, es muy poco probable que agreguemos JIT. Hay algunas otras ideas en la discusión pero ninguna conclusión.

Dado que estamos moviendo la propuesta al rastreador GIP, cerraré esta y, si alguien tiene una idea, abra una nueva propuesta (después de ver si nadie más lo hizo primero). Este tema se puede vincular en propuestas relevantes para mantener el historial de discusión.

Algunos colaboradores tienen ideas diferentes para la compilación AOT, pero les dejaré abrir una propuesta con lo que tienen en mente.

@vnen
Entonces actualice la hoja de ruta, está engañando a la gente.

Bueno, incluso olvidé que existía la hoja de ruta, no se actualiza en mucho tiempo.

@vnen
Lo olvidaste, pero otros usuarios no. Así que por favor actualícenlo, porque muchos lo tienen en cuenta.

los intérpretes son basura y para mí eso mata la utilidad que tiene gd script cuando el rendimiento es pésimo

usa un intérprete cuando está escribiendo un emulador básico, no un motor de juego completo

un compilador justo a tiempo a menudo puede ser más rápido que un compilador trasero lento y desactualizado de todos modos, el sistema de interpretación es simple y llanamente malo, y siento que preferiría eliminar el script gd e ir exclusivamente con c ++, y simplemente hacer un configuración de gueto con un sistema justo a tiempo

Creo que la gente no está dispuesta a admitir que el intérprete es una mierda ya que la gente no quiere herirse los sentimientos de los demás por alguna razón, pero comparado con C++, el resultado final es bonito, ehhh...

es... está bien si tienes algo realmente básico, supongo, pero, para algo serio, es solo, como si no fuera adecuado para casi nada en mi mente, más allá de los juegos realmente simples.

@RaTcHeT302 no es necesario que traigas tu negatividad y recuerda que tenemos un Código de Conducta . Hay planes para optimizar GDScript de muchas maneras, incluida la compilación en código nativo, pero no con JIT. Además, si compara con C ++, incluso un tiempo de ejecución JITed será lento.

@vnen : Solo

JIT no es posible en todas las plataformas principales. Es mejor concentrar los esfuerzos
donde todos puedan aprovecharlos. La compilación anticipada es la única
Tecnología 100% compatible.

El domingo 9 de agosto de 2020 a las 8:46 a. m., Zireael07 [email protected] escribió:

@vnen https://github.com/vnen : Solo digo que LuaJIT casi ha terminado
allí con C/C++ en los puntos de referencia de rendimiento, pero hay una amplia
creencia de que la magia está involucrada 😄


Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-671047838 ,
o darse de baja
https://github.com/notifications/unsubscribe-auth/AAUFIAGK3S7RA3DRXW6NUC3R72LCVANCNFSM4CFZMZPA
.

¿No hay desventajas en AoT en comparación con JIT? Por ejemplo, ¿qué pasa con los scripts generados o cargados dinámicamente? JIT no debería tener problemas para optimizarlos, pero ¿AoT?

Por cierto, ¿en qué plataforma principal no es posible JIT? Pensé que Java con su JIT se ejecuta prácticamente en todas partes.

Editar: Oh, supongo que Apple es el problema, el que prohíbe el uso de JIT. Por lo tanto, es una limitación de bloqueo de proveedor arbitraria, no técnica. Me alegro de no tener que admitir ningún dispositivo Apple...

iOS no permite JIT para aplicaciones distribuidas a través de la App Store
a menos que esté utilizando el componente del navegador de Apple.

AOT no sería posible para scripts dinámicos.

El domingo 9 de agosto de 2020 a las 11:15 a. m. monnef [email protected] escribió:

¿No hay desventajas en AoT en comparación con JIT? por ejemplo que tal
scripts generados o cargados dinámicamente? JIT no debería tener un problema
optimizándolos, pero AoT?

Por cierto, ¿en qué plataforma principal no es posible JIT? Pensé Java
con su JIT se ejecuta prácticamente en todas partes.


Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-671064117 ,
o darse de baja
https://github.com/notifications/unsubscribe-auth/AAUFIACWWCWDXO4NZ7GCL63R724RHANCNFSM4CFZMZPA
.

iOS, Web y algunas plataformas de consola no admiten la compilación JIT.

Hay un rumor de que AOT ahora se descarta con JIT. Espero que eso no sea cierto. Alguien, por favor dígame, ¿es verdad?

¿Está diciendo que hay un rumor de que AOT ya no estaría permitido en algunos
¿dispositivos? Eso es literalmente imposible, a menos que esté malinterpretando algo.

El miércoles 30 de septiembre de 2020 a las 5:47 a. m. Teashrock [email protected] escribió:

>
>

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


Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-701313134 ,
o darse de baja
https://github.com/notifications/unsubscribe-auth/AAUFIADVHAXMGBOL3YEDVILSIMEEFANCNFSM4CFZMZPA
.

@pchasco
No. Quiero decir, alguien me dijo que GDScript no tendrá ni AOT ni JIT.

Tal vez no sea compatible oficialmente, pero puedo confirmar que el nuevo
La arquitectura GDScript descrita por vnen es capaz de soportarlo. Un
desarrollador emprendedor podría implementar un módulo personalizado para hacer AOT para
C nativo de GDN

El miércoles 30 de septiembre de 2020 a las 11:16 a. m. Teashrock [email protected] escribió:

>
>

@pchasco https://github.com/pchasco

No. Quiero decir, alguien me dijo que GDScript no tendrá ni AOT ni JIT.


Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-701494412 ,
o darse de baja
https://github.com/notifications/unsubscribe-auth/AAUFIACAKZVJQK2CHTDJT63SINKXDANCNFSM4CFZMZPA
.

@pchasco

quizás

Entonces, no lo sabes. "Capaz de soportar" no significa "soportará".

@Teashrock No sabemos si GDScript contará con la compilación JIT o AOT en 4.0, pero no es muy probable. Tal vez en 4.1 o posterior...

Si necesita un mayor rendimiento para procesar números (es decir, las limitaciones de su CPU provienen claramente del lenguaje de secuencias de comandos), use C#. Pero primero, lea los tutoriales de optimización :slightly_smiling_face:

@Calinou
Es solo que hablaron sobre 4.0 con respecto a JIT/AOT. Y ahora me estás hablando de 4.1. ¿Porque eso? ¿Cómo es eso? Alguien, solo dé a todos una respuesta pública clara, que no estará en algún lugar profundo de GitHub.

Gracias de antemano.

¿Fue útil esta página
0 / 5 - 0 calificaciones