Explicación de la compilación justo a tiempo

La compilación justo a tiempo es un método para mejorar el rendimiento de los programas interpretados. Durante la ejecución, el programa puede compilarse en código nativo para mejorar su rendimiento. También se conoce como compilación dinámica.

La compilación dinámica tiene algunas ventajas sobre la compilación estática. Al ejecutar aplicaciones Java o C #, el entorno de ejecución puede perfilar la aplicación mientras se ejecuta. Esto permite generar un código más optimizado. Si el comportamiento de la aplicación cambia mientras se está ejecutando, el entorno de ejecución puede volver a compilar el código.

Algunas de las desventajas incluyen retrasos en el inicio y la sobrecarga de compilación durante el tiempo de ejecución. Para limitar la sobrecarga, muchos compiladores JIT solo compilan las rutas de código que se utilizan con frecuencia.

Visión general

Tradicionalmente, existen dos métodos para convertir el código fuente en un formulario que se puede ejecutar en una plataforma. La compilación estática convierte el código en un lenguaje para una plataforma específica. Un intérprete ejecuta directamente el código fuente.

La compilación JIT intenta aprovechar los beneficios de ambos. Mientras se ejecuta el programa interpretado, el compilador JIT determina el código utilizado con más frecuencia y lo compila en código máquina. Dependiendo del compilador, esto se puede hacer en un método o en una sección de código más pequeña.

La compilación dinámica se describió por primera vez en un artículo de J. McCarthy sobre LISP en 1960.

Just In Time Compilation, JIT o Dynamic Translation, es una compilación que se realiza durante la ejecución de un programa. Es decir, en tiempo de ejecución, a diferencia de antes de la ejecución. Lo que pasa es la traducción a código máquina. Las ventajas de un JIT se deben al hecho de que, dado que la compilación tiene lugar en tiempo de ejecución, un compilador JIT tiene acceso a información dinámica en tiempo de ejecución que le permite realizar mejores optimizaciones (como funciones en línea).

Lo que es importante entender sobre la compilación JIT es que compilará el código de bytes en las instrucciones del código de máquina de la máquina en ejecución. Es decir, que el código de máquina resultante está optimizado para la arquitectura de CPU de la máquina en ejecución.

Algunos ejemplos de compiladores JIT son JVM (Java Virtual Machine) en Java y CLR (Common Language Runtime), en C #.

Historia

Al principio, un compilador era responsable de convertir un lenguaje de alto nivel (definido como un nivel superior al ensamblador) en código objeto (instrucciones de máquina), que luego sería vinculado (por un enlazador) en un ejecutable.

En un momento de la evolución de los lenguajes, los compiladores compilarían un lenguaje de alto nivel en pseudocódigo, que luego sería interpretado (por un intérprete) para ejecutar su programa. Esto eliminó el código objeto y los ejecutables, y permitió que estos lenguajes fueran portables a múltiples sistemas operativos y plataformas de hardware. Pascal (que se compiló en P-Code) fue uno de los primeros; Java y C # son ejemplos más recientes. Finalmente, el término P-Code fue reemplazado por bytecode, ya que la mayoría de las pseudooperaciones tienen un byte de longitud.

Un compilador Just-In-Time (JIT) es una característica del intérprete en tiempo de ejecución, que en lugar de interpretar el código de bytes cada vez que se invoca un método, compilará el código de bytes en las instrucciones del código de máquina de la máquina en ejecución y luego lo invocará. código objeto en su lugar. Idealmente, la eficiencia de ejecutar el código objeto superará la ineficacia de volver a compilar el programa cada vez que se ejecuta.

Escenario típico

El código fuente se convierte completamente en código máquina.

Escenario JIT

El código fuente se convertirá en una estructura similar a un lenguaje ensamblador [por ejemplo, IL (lenguaje intermedio) para C #, ByteCode para java].

El código intermedio se convierte a lenguaje de máquina solo cuando la aplicación necesita que los códigos requeridos solo se conviertan a código de máquina.

Comparación JIT vs Non-JIT

En JIT, no todo el código se convierte en código de máquina, primero una parte del código que es necesaria se convertirá en código de máquina, luego, si un método o funcionalidad llamada no está en la máquina, entonces se convertirá en código de máquina, lo que reduce la carga en la CPU. Como el código de máquina se generará en tiempo de ejecución, el compilador JIT producirá código de máquina optimizado para ejecutar la arquitectura de CPU de la máquina.

Algunos ejemplos de JIT son:

  • Java: JVM (máquina virtual Java)
  • C #: CLR (Common Language Runtime)
  • Android: DVM (Dalvik Virtual Machine) o ART (Android RunTime) en versiones más recientes

La máquina virtual Java (JVM) ejecuta bytecode y mantiene un recuento de cuántas veces se ejecuta una función. Si este recuento excede un límite predefinido, JIT compila el código en lenguaje de máquina que puede ser ejecutado directamente por el procesador (a diferencia del caso normal en el que javac compila el código en bytecode y luego en Java, el intérprete interpreta este bytecode línea por línea lo convierte en código máquina y se ejecuta).

Además, la próxima vez que se calcula esta función, se vuelve a ejecutar el mismo código compilado, a diferencia de la interpretación normal en la que el código se vuelve a interpretar línea por línea. Esto hace que la ejecución sea más rápida.