En los inicios de Java, el rendimiento era lento porque el bytecode resultado de la compilación del código fuente era interpretado por la JVM, instrucción a instrucción. Por ello se decide introducir una compilación a código nativo en tiempo de ejecución.
Los compiladores JIT aceptan bytecode de la VM y producen código nativo en tiempo de ejecución Código fuente Compilación Bytecode JIT Código nativo Tiempo de ejecución
en base a ello, permite realizar optimizaciones Elimina código inalcanzable Sustituye expresiones constantes por su valor calculado Reemplaza llamadas a métodos por su definición Puede deshacer optimizaciones que no tienen el efecto esperado
Genera código nativo a partir del bytecode mediante un analisis puramente estático, sin depender de una ejecución previa. Código fuente Compilación Bytecode AOT Código nativo
por el comportamiento dinámico de la aplicación Se necesita compilar para cada arquitectura No soporta fácilmente técnicas de metaprogramación (reflection, anotaciones…)
Mejores herramientas para desarrollo y depuración Errores en C++ pueden provocar cuelgues en la JVM e incluso fallos de seguridad (☣ ☣ ) mientras que Java es un lenguaje seguro Mejoras en la generación de código de Graal pueden beneficiar al propio compilador Buffer Overflow
Java 9 ( ) Interfaz estándar por la cual la JVM se comunica con los compiladores JIT (Graal en nuestro caso) Permite a cualquiera implementar su propio JIT JEP 243 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler -Djvmci.Compiler=<name of compiler>
tiempo de inicio de la JVM Graal está escrito en Java, eso significa que tiene que compilar su propio bytecode Hasta que no se compile a sí mismo, se estará ejecutando en modo interpretado y funcionará lentamente
AOT Genera ejecutables autocontenidos (“imágenes nativas”) “Hipótesis de mundo cerrado”: todas las clases que existan en tiempo de ejecución tienen que ser accesibles en el momento de la compilación
Foo foo = new Foo(); URLClassLoader loader = new URLClassLoader(new URL[] { ... }); Class c = loader.loadClass("Bar"); Object bar = c.newInstance(); } $ native-image Main
las clases existentes, el bytecode es compilado a código nativo usando Graal El ejecutable final incluye Substrate VM como “runtime” No se pueden usar profilers, depuradores… Inicializadores estáticos durante el build, no en tiempo de ejecución Limitaciones
cero. Herramientas compartidas, por ejemplo depuradores y profilers. Intérpretes de alto rendimiento. Fácil interoperación entre lenguajes (poliglotismo).
syntax trees, ASTs). Cada elemento semántico del lenguaje corresponde a una clase nodo. Para añadir un nuevo lenguaje, definimos los nodos de su AST con la ayuda de la API de Truffle. También tenemos que definir métodos de evaluación para cada tipo de nodo.
las optimizaciones Especialización de nodos polimórficos Evaluación parcial de subárboles + z 5 A los creadores de nuevos lenguajes invitados les basta con conocer la API de Truffle
def go_down() @val-=1 end end foos = [Foo.new, Foo.new, Foo.new] loop do foos.each do |x| x.go_up() x.go_down() end end $ ruby --jvm Foo.rb $ polyglot --language ruby --jvm Foo.rb
def go_down() @val-=1 end end foos = [Foo.new, Foo.new, Foo.new] loop do foos.each do |x| x.go_up() x.go_down() end end $ ruby --jvm --vm.Dgraal.Dump=:1 --vm.Dgraal.PrintGraph=Network Foo.rb
to Bind Them Everything you need to know about GraalVM Twitter's Quest for a Wholly Graal Runtime GraalVM JIT versus HotSpot JIT C2 Lessons Learned with Truffle and Graal Building a DSL with GraalVM Project Sulong: an LLVM bitcode interpreter Ten Things You Can Do With GraalVM - Oleg Šelajev
evaluation for high-performance dynamic language runtimes Self-Optimizing AST Interpreters Sulong: Memory Safe and Efficient Execution of LLVM-Based Languages