De Wikipedia, la enciclopedia libre
Saltar a navegación Saltar a búsqueda

En informática , la compilación justo a tiempo ( JIT ) (también traducción dinámica o compilaciones en tiempo de ejecución ) [1] es una forma de ejecutar código informático que implica la compilación durante la ejecución de un programa, en tiempo de ejecución , en lugar de antes de la ejecución. [2] La mayoría de las veces, consiste en código fuente o, más comúnmente, traducción de código de bytes a código de máquina., que luego se ejecuta directamente. Un sistema que implementa un compilador JIT generalmente analiza continuamente el código que se está ejecutando e identifica partes del código donde la aceleración obtenida de la compilación o recompilación superaría la sobrecarga de compilar ese código.

La compilación JIT es una combinación de los dos enfoques tradicionales de traducción a código de máquina: compilación anticipada (AOT) e interpretación , y combina algunas ventajas e inconvenientes de ambos. [2] Aproximadamente, la compilación JIT combina la velocidad del código compilado con la flexibilidad de la interpretación, con la sobrecarga de un intérprete y la sobrecarga adicional de compilar y vincular (no solo interpretar). La compilación JIT es una forma de compilación dinámica y permite la optimización adaptativa , como la recopilación dinámica y las aceleraciones específicas de la microarquitectura [nb 1] [3]La interpretación y la compilación JIT son especialmente adecuadas para lenguajes de programación dinámicos , ya que el sistema de tiempo de ejecución puede manejar tipos de datos vinculados en última instancia y hacer cumplir las garantías de seguridad.

Aplicaciones [ editar ]

La compilación JIT se puede aplicar a algunos programas, o se puede utilizar para ciertas capacidades, particularmente capacidades dinámicas como expresiones regulares . Por ejemplo, un editor de texto puede compilar una expresión regular proporcionada en tiempo de ejecución en código de máquina para permitir una coincidencia más rápida; esto no se puede hacer con anticipación, ya que el patrón solo se proporciona en tiempo de ejecución. Varios entornos de ejecución modernos se basan en la compilación JIT para la ejecución de código de alta velocidad, incluida la mayoría de las implementaciones de Java , junto con .NET Framework de Microsoft .. De manera similar, muchas bibliotecas de expresiones regulares cuentan con compilación JIT de expresiones regulares, ya sea en código de bytes o en código de máquina. La compilación JIT también se utiliza en algunos emuladores para traducir el código de máquina de una arquitectura de CPU a otra.

Una implementación común de la compilación JIT es tener primero la compilación AOT en código de bytes ( código de máquina virtual ), conocida como compilación de código de bytes , y luego tener la compilación JIT en código de máquina (compilación dinámica), en lugar de la interpretación del código de bytes. Esto mejora el rendimiento en tiempo de ejecución en comparación con la interpretación, a costa del retraso debido a la compilación. Los compiladores JIT traducen continuamente, como con los intérpretes, pero el almacenamiento en caché del código compilado minimiza el retraso en la ejecución futura del mismo código durante una ejecución determinada. Dado que solo se compila una parte del programa, hay un retraso significativamente menor que si se compilara todo el programa antes de la ejecución.

Resumen [ editar ]

En un sistema compilado por código de bytes , el código fuente se traduce a una representación intermedia conocida como código de bytes . Bytecode no es el código de máquina para ninguna computadora en particular, y puede ser portátil entre arquitecturas de computadora. El código de bytes puede entonces ser interpretado o ejecutado por una máquina virtual . El compilador JIT lee los códigos de bytes en muchas secciones (o en su totalidad, rara vez) y los compila dinámicamente en código de máquina para que el programa pueda ejecutarse más rápido. Esto se puede hacer por archivo, por función o incluso en cualquier fragmento de código arbitrario; el código se puede compilar cuando está a punto de ejecutarse (de ahí el nombre "justo a tiempo"), y luego se puede almacenar en caché y reutilizar sin necesidad de volver a compilarlo.

Por el contrario, una máquina virtual interpretada tradicional simplemente interpretará el código de bytes, generalmente con un rendimiento mucho menor. Algunos intérpretes incluso interpretan el código fuente, sin el paso de compilar primero en código de bytes, con un rendimiento aún peor. El código compilado estáticamente o el código nativo se compila antes de la implementación. Un entorno de compilación dinámicoes uno en el que se puede utilizar el compilador durante la ejecución. Un objetivo común del uso de técnicas JIT es alcanzar o superar el rendimiento de la compilación estática, mientras se mantienen las ventajas de la interpretación del código de bytes: Gran parte del "trabajo pesado" de analizar el código fuente original y realizar la optimización básica a menudo se maneja en tiempo de compilación. antes de la implementación: la compilación de código de bytes a código de máquina es mucho más rápida que la compilación de código fuente. El código de bytes implementado es portátil, a diferencia del código nativo. Dado que el tiempo de ejecución tiene control sobre la compilación, al igual que el código de bytes interpretado, puede ejecutarse en una caja de arena segura. Los compiladores de código de bytes a código de máquina son más fáciles de escribir, porque el compilador de código de bytes portátil ya ha realizado gran parte del trabajo.

El código JIT generalmente ofrece un rendimiento mucho mejor que los intérpretes. Además, en algunos casos puede ofrecer un mejor rendimiento que la compilación estática, ya que muchas optimizaciones solo son factibles en tiempo de ejecución: [4] [5]

  1. La compilación se puede optimizar para la CPU de destino y el modelo del sistema operativo donde se ejecuta la aplicación. Por ejemplo, JIT puede elegir instrucciones de CPU de vector SSE2 cuando detecta que la CPU las admite. Para obtener este nivel de especificidad de optimización con un compilador estático, se debe compilar un binario para cada plataforma / arquitectura prevista, o bien incluir múltiples versiones de partes del código dentro de un solo binario.
  2. El sistema puede recopilar estadísticas sobre cómo se está ejecutando realmente el programa en el entorno en el que se encuentra, y puede reorganizar y recompilar para obtener un rendimiento óptimo. Sin embargo, algunos compiladores estáticos también pueden tomar información de perfil como entrada.
  3. El sistema puede realizar optimizaciones de código global (por ejemplo, inserción de funciones de biblioteca) sin perder las ventajas de la vinculación dinámica y sin los gastos generales inherentes a los compiladores y vinculadores estáticos. Específicamente, al realizar sustituciones globales en línea, un proceso de compilación estático puede necesitar verificaciones en tiempo de ejecución y garantizar que se produzca una llamada virtual si la clase real del objeto anula el método en línea, y es posible que deban procesarse las verificaciones de condición de límite en los accesos a la matriz. dentro de los bucles. Con la compilación justo a tiempo, en muchos casos, este procesamiento se puede mover fuera de los bucles, lo que a menudo proporciona grandes aumentos de velocidad.
  4. Aunque esto es posible con lenguajes de recolección de basura compilados estáticamente, un sistema de código de bytes puede reorganizar más fácilmente el código ejecutado para una mejor utilización de la caché.

Debido a que un JIT debe representar y ejecutar una imagen binaria nativa en tiempo de ejecución, los JIT de código máquina verdaderos necesitan plataformas que permitan que los datos se ejecuten en tiempo de ejecución, lo que hace que el uso de dichos JIT en una máquina basada en la arquitectura de Harvard sea imposible; lo mismo puede decirse de ciertos sistemas operativos y máquinas virtuales también. Sin embargo, un tipo especial de "JIT" puede potencialmente no apuntar arquitectura de la CPU de la máquina física, sino más bien un código de bytes VM optimizado donde prevalecen limitaciones en código de máquina en bruto, especialmente cuando la VM de código de bytes que finalmente aprovecha una JIT a código nativo. [6]

Retraso de inicio y optimizaciones [ editar ]

JIT provoca un retraso de leve a notable en la ejecución inicial de una aplicación, debido al tiempo necesario para cargar y compilar el código de bytes. A veces, este retraso se denomina "retraso de tiempo de inicio" o "tiempo de calentamiento". En general, cuanta más optimización realice JIT, mejor será el código que generará, pero el retraso inicial también aumentará. Por lo tanto, un compilador JIT tiene que hacer una compensación entre el tiempo de compilación y la calidad del código que espera generar. El tiempo de inicio puede incluir un aumento de operaciones vinculadas a IO además de la compilación JIT: por ejemplo, el archivo de datos de clase rt.jar para la máquina virtual Java (JVM) es de 40 MB y la JVM debe buscar una gran cantidad de datos en este archivo contextualmente enorme. . [7]

Una posible optimización, utilizada por la máquina virtual Java HotSpot de Sun , es combinar la interpretación y la compilación JIT. El código de la aplicación se interpreta inicialmente, pero la JVM supervisa qué secuencias de código de bytesse ejecutan con frecuencia y los traduce a código de máquina para su ejecución directa en el hardware. Para el código de bytes que se ejecuta solo unas pocas veces, esto ahorra tiempo de compilación y reduce la latencia inicial; para el código de bytes ejecutado con frecuencia, la compilación JIT se utiliza para ejecutarse a alta velocidad, después de una fase inicial de interpretación lenta. Además, dado que un programa pasa la mayor parte del tiempo ejecutando una minoría de su código, el tiempo de compilación reducido es significativo. Finalmente, durante la interpretación inicial del código, se pueden recopilar estadísticas de ejecución antes de la compilación, lo que ayuda a realizar una mejor optimización. [8]

La compensación correcta puede variar según las circunstancias. Por ejemplo, la máquina virtual Java de Sun tiene dos modos principales: cliente y servidor. En el modo cliente, se realiza una compilación y optimización mínimas para reducir el tiempo de inicio. En el modo de servidor, se realiza una compilación y optimización extensivas para maximizar el rendimiento una vez que la aplicación se está ejecutando sacrificando el tiempo de inicio. Otros compiladores Just-In-Time de Java han utilizado una medición en tiempo de ejecución del número de veces que se ha ejecutado un método combinado con el tamaño del código de bytes de un método como heurística para decidir cuándo compilar. [9] Otro más usa el número de veces ejecutado combinado con la detección de bucles. [10]En general, es mucho más difícil predecir con precisión qué métodos optimizar en aplicaciones de ejecución corta que en aplicaciones de ejecución prolongada. [11]

Native Image Generator (Ngen) de Microsoft es otro enfoque para reducir el retraso inicial. [12] Ngen precompila (o "pre-JIT") el código de bytes en una imagen de Common Intermediate Language en código nativo de la máquina. Como resultado, no se necesita compilación en tiempo de ejecución. .NET Framework 2.0 incluido con Visual Studio 2005 ejecuta Ngen en todas las DLL de la biblioteca de Microsoft inmediatamente después de la instalación. Pre-jitting proporciona una forma de mejorar el tiempo de inicio. Sin embargo, la calidad del código que genera podría no ser tan buena como la del JIT, por las mismas razones por las que el código se compila estáticamente, sin optimización guiada por perfiles., no puede ser tan bueno como el código compilado JIT en el caso extremo: la falta de datos de creación de perfiles para impulsar, por ejemplo, el almacenamiento en caché en línea. [13]

También existen implementaciones de Java que combinan un compilador AOT (adelantado) con un compilador JIT ( Excelsior JET ) o un intérprete ( compilador GNU para Java ).

Historia [ editar ]

El compilador JIT publicado más temprano generalmente se atribuye al trabajo en LISP por John McCarthy en 1960. [14] En su artículo seminal Funciones recursivas de expresiones simbólicas y su cálculo por máquina, Parte I , menciona funciones que se traducen durante el tiempo de ejecución, por lo que ahorra la necesidad de guardar la salida del compilador en tarjetas perforadas [15] (aunque esto se conocería más exactamente como un " sistema Compile and go "). Otro ejemplo temprano fue el de Ken Thompson , quien en 1968 dio una de las primeras aplicaciones de expresiones regulares , aquí para la coincidencia de patrones en el editor de texto QED.. [16] Para mayor velocidad, Thompson implementó la coincidencia de expresiones regulares mediante JITing con el código IBM 7094 en el Sistema de tiempo compartido compatible . [14] Una técnica influyente para derivar código compilado a partir de la interpretación fue iniciada por James G. Mitchell en 1970, que implementó para el lenguaje experimental LC² . [17] [18]

Smalltalk (c. 1983) fue pionero en nuevos aspectos de las compilaciones JIT. Por ejemplo, la traducción a código de máquina se realizó bajo demanda y el resultado se almacenó en caché para su uso posterior. Cuando la memoria escaseaba, el sistema borraba parte de este código y lo regeneraba cuando era necesario de nuevo. [2] [19] El lenguaje Sun's Self mejoró estas técnicas ampliamente y fue en un momento el sistema Smalltalk más rápido del mundo; logrando hasta la mitad de la velocidad de C optimizado [20] pero con un lenguaje totalmente orientado a objetos.

Self fue abandonado por Sun, pero la investigación se centró en el lenguaje Java. El término "compilación Just-in-time" fue tomado del término de fabricación " Just in time " y popularizado por Java, con James Gosling usando el término de 1993. [21] Actualmente, la mayoría de las implementaciones de Java Virtual Machine utilizan JITing. , ya que HotSpot se basa y utiliza ampliamente esta base de investigación.

El proyecto de HP Dynamo [22] era un compilador JIT experimental donde el formato 'bytecode' y el formato del código de máquina eran el mismo; el sistema convirtió el código de máquina PA-6000 en código de máquina PA-8000 . Contrariamente a la intuición, esto resultó en aceleraciones, en algunos casos del 30%, ya que hacer esto permitió optimizaciones a nivel de código de máquina, por ejemplo, código integrado para un mejor uso de la caché y optimizaciones de llamadas a bibliotecas dinámicas y muchas otras optimizaciones en tiempo de ejecución que son convencionales. los compiladores no pueden intentarlo. [23] [24]

En noviembre de 2020, PHP 8.0 introdujo un compilador JIT. [25]

Seguridad [ editar ]

La compilación JIT utiliza fundamentalmente datos ejecutables y, por lo tanto, plantea desafíos de seguridad y posibles vulnerabilidades.

La implementación de la compilación JIT consiste en compilar código fuente o código de bytes en código de máquina y ejecutarlo. Esto generalmente se hace directamente en la memoria: el compilador JIT genera el código de la máquina directamente en la memoria y lo ejecuta inmediatamente, en lugar de enviarlo al disco y luego invocar el código como un programa separado, como es habitual antes de la compilación. En las arquitecturas modernas, esto presenta un problema debido a la protección del espacio ejecutable : la memoria arbitraria no se puede ejecutar, ya que de lo contrario existe un potencial agujero de seguridad. Por tanto, la memoria debe marcarse como ejecutable; por razones de seguridad, esto debe hacerse después de que el código se haya escrito en la memoria y marcado como de solo lectura, ya que la memoria de escritura / ejecutable es un agujero de seguridad (consulte W ^ X ).[26] Por ejemplo, el compilador JIT de Firefox para Javascript introdujo esta protección en una versión de lanzamiento con Firefox 46. [27]

La fumigación JIT es una clase de exploits de seguridad informática que utilizan la compilación JIT para la fumigación de pilas : la memoria resultante es ejecutable, lo que permite una vulnerabilidad si la ejecución se puede mover al montón.

Ver también [ editar ]

  • Traducción binaria
  • HotSpot
  • Common Language Runtime
  • Crusoe , un microprocesador que esencialmente realiza la compilación justo a tiempo desde el código x86 al microcódigo dentro del microprocesador.
  • GNU lightning : una biblioteca que genera código en lenguaje ensamblador en tiempo de ejecución
  • LLVM
  • Código auto modificable
  • Seguimiento de la compilación justo a tiempo
  • Compilación dinámica

Notas [ editar ]

  1. ^ Los compiladores de Ahead-of-Time también pueden apuntar a microarquitecturas específicas, pero la diferencia entre AOT y JIT en ese asunto es de portabilidad. Un JIT puede generar código adaptado a la CPU que se está ejecutando actualmente en tiempo de ejecución, mientras que un AOT, en lugar de optimizar para un subconjunto generalizado de uarches, debe conocer la CPU de destino de antemano; dicho código puede no solo no funcionar en otros tipos de CPU, sino que puede ser completamente inestable.

Referencias [ editar ]

Citas [ editar ]

  1. ^ Idiomas, compiladores y sistemas de tiempo de ejecución , Universidad de Michigan, Ciencias de la computación e ingeniería , consultado el 15 de marzo de 2018
  2. ↑ a b c Aycock, 2003 .
  3. ^ "¿El JIT aprovecha mi CPU?" . WebLog de David Notario . Consultado el 3 de diciembre de 2018 .
  4. ^ Croce, Louis. "Compilación Just in Time" (PDF) . Universidad de Columbia . Archivado desde el original (PDF) el 2018-05-03.
  5. ^ "¿Cuáles son las ventajas de la compilación JIT vs. AOT?" . Desbordamiento de pila . 21 de enero de 2010.
  6. ^ "javascript: compile un idioma basado en JIT en Webassembly" . Desbordamiento de pila . Consultado el 4 de diciembre de 2018 .
  7. ^ Haase, Chet (mayo de 2007). "Consumidor JRE: tecnología Java más ágil, más mala" . Sun Microsystems . Consultado el 27 de julio de 2007 . A nivel del sistema operativo, todos estos megabytes deben leerse desde el disco, lo cual es una operación muy lenta. En realidad, es el tiempo de búsqueda del disco lo que mata; leer archivos grandes de forma secuencial es relativamente rápido, pero buscar los bits que realmente necesitamos no lo es. Entonces, aunque solo necesitamos una pequeña fracción de los datos en estos archivos grandes para cualquier aplicación en particular, el hecho de que estemos buscando en todos los archivos significa que hay mucha actividad en el disco.
  8. ^ "La arquitectura del motor de rendimiento de Java HotSpot" . Oracle.com . Consultado el 5 de julio de 2013 .
  9. ^ Schilling, Jonathan L. (febrero de 2003). "La heurística más simple puede ser la mejor en los compiladores JIT de Java" (PDF) . Avisos SIGPLAN . 38 (2): 36–46. doi : 10.1145 / 772970.772975 . Archivado desde el original (PDF) el 24 de septiembre de 2015.
  10. ^ Toshio Suganuma, Toshiaki Yasue, Motohiro Kawahito, Hideaki Komatsu, Toshio Nakatani, "Un marco de optimización dinámica para un compilador justo a tiempo de Java", Actas de la 16a conferencia ACM SIGPLAN sobre programación orientada a objetos, sistemas, lenguajes y aplicaciones (OOPSLA '01), págs. 180-195, 14-18 de octubre de 2001.
  11. ^ Matthew Arnold, Michael Hind, Barbara G. Ryder, "Un estudio empírico de optimización selectiva", Actas del XIII Taller internacional sobre lenguajes y compiladores de artículos revisados ​​de computación paralela , págs. 49-67, 10-12 de agosto de 2000 .
  12. ^ "Generador de imágenes nativas (Ngen.exe)" . Msdn2.microsoft.com . Consultado el 5 de julio de 2013 .
  13. ^ Matthew R. Arnold, Stephen Fink, David P. Grove, Michael Hind y Peter F. Sweeney, " Una encuesta de optimización adaptativa en máquinas virtuales ", Actas de la IEEE , 92 (2), febrero de 2005, págs. 449 –466.
  14. ^ a b Aycock 2003 , 2. Técnicas de compilación JIT, 2.1 Génesis, p. 98.
  15. ^ McCarthy, J. (abril de 1960). "Funciones recursivas de expresiones simbólicas y su computación por máquina, Parte I". Comunicaciones de la ACM . 3 (4): 184-195. CiteSeerX 10.1.1.111.8833 . doi : 10.1145 / 367177.367199 . 
  16. ^ Thompson, 1968 .
  17. ^ Aycock 2003 , 2. Técnicas de compilación JIT, 2.2 LC², p. 98–99.
  18. ^ Mitchell, JG (1970). "El diseño y construcción de sistemas de programación interactivos flexibles y eficientes". Cite journal requiere |journal=( ayuda )
  19. ^ Deutsch, LP; Schiffman, AM (1984). "Implementación eficiente del sistema Smalltalk-80" (PDF) . POPL '84: Actas del 11º Simposio ACM SIGACT-SIGPLAN sobre principios de lenguajes de programación : 297–302. doi : 10.1145 / 800017.800542 . ISBN  0-89791-125-3. Archivado desde el original (PDF) el 18 de junio de 2004.
  20. ^ [1] Archivado el 24 de noviembre de 2006 en la Wayback Machine.
  21. ^ Aycock 2003 , 2.14 Java, p. 107, nota al pie 13.
  22. ^ "Dynamo: A Transparent Dynamic Optimization System" Vasanth Bala, Evelyn Duesterwald, Sanjeev Banerjia - PLDI '00 Actas de la conferencia ACM SIGPLAN 2000 sobre diseño e implementación de lenguajes de programación - páginas 1 a 12 - doi : 10.1145 / 349299.349303 . Consultado el 28 de marzo de 2012
  23. ^ John Jannotti. "HP's Dynamo - Página 1 - (3/2000)" . Ars Technica . Consultado el 5 de julio de 2013 .
  24. ^ "El proyecto HP Dynamo" . Archivado desde el original el 19 de octubre de 2002 . Consultado el 12 de abril de 2016 .CS1 maint: URL no apta ( enlace )
  25. ^ Tung, Liam (27 de noviembre de 2020). "Lanzamiento del lenguaje de programación PHP 8: este nuevo compilador JIT apunta a un mejor rendimiento" . ZDNet . Consultado el 28 de noviembre de 2020 .
  26. ^ " Cómo JIT - una introducción ", Eli Bendersky, 5 de noviembre de 2013 a las 5:59 am
  27. ^ De Mooij, enero "W ^ X JIT-code activado en Firefox" . Jan De Mooij . Consultado el 11 de mayo de 2016 .

Fuentes [ editar ]

  • Aycock, J. (junio de 2003). "Una breve historia del just-in-time". Encuestas de computación ACM . 35 (2): 97-113. CiteSeerX  10.1.1.97.3985 . doi : 10.1145 / 857076.857077 .
  • Thompson, K. (1968). "Técnicas de programación: algoritmo de búsqueda de expresiones regulares". Comunicaciones de la ACM . 11 (6): 419–422. doi : 10.1145 / 363347.363387 .

Enlaces externos [ editar ]

  • Entrada del Diccionario de Computación en línea gratuito
  • Mozilla Nanojit : una pequeña biblioteca de C ++ multiplataforma que emite código de máquina. Se utiliza como JIT para los motores Javascript de Mozilla Tamarin y SpiderMonkey .
  • OVPsim , una herramienta JIT central integrada que convierte ARM, MIPS y otras instrucciones ISA a x86 para ejecución / simulación
  • Creación de perfiles de código generado e interpretado en tiempo de ejecución mediante VTune Performance Analyzer