¿Cómo funciona Chrome V8? ¿Y por qué no se compiló JavaScript JIT en primer lugar?

19

He estado investigando intérpretes / compiladores, luego me topé con JIT-Compilation, específicamente el motor Javascript V8 de Google Chrome.

Mis preguntas son

  1. ¿Cómo puede ser más rápido que la interpretación estándar?
  2. ¿Por qué no se utilizó JIT-Compilation en primer lugar?


Mi comprensión actual

  1. Cada programa Javascript comienza como código fuente , luego, independientemente del método de ejecución, finalmente se traduce al código de la máquina .
    Tanto JIT-Compilation como Interpretation deben seguir este camino, entonces, ¿cómo puede JIT-Compilation ser más rápido (también porque JIT tiene una limitación de tiempo, a diferencia de AOT-Compilation)?

  2. Parece que la compilación JIT es una innovación relativamente antigua , basada en el artículo de compilación JIT de Wikipedia .

"El primer compilador JIT publicado se atribuye generalmente al trabajo en LISP por McCarthy en 1960 ".

"Smalltalk (c. 1983 ) fue pionero en nuevos aspectos de las compilaciones JIT. Por ejemplo, la traducción al código de la máquina se realizó bajo demanda, y el resultado se almacenó en caché para su uso posterior. Cuando la memoria escaseaba, el sistema eliminaría parte de este código y se regeneraría cuando fue necesario de nuevo ".

Entonces, ¿por qué se interpretó Javascript para empezar ?


Estoy muy confundido y he investigado mucho sobre esto, pero no he encontrado respuestas satisfactorias.

Entonces, se agradecerían respuestas claras y concisas. Y si se necesita una explicación adicional sobre intérpretes, compiladores JIT, etc., eso también se agradece.

Anton Paras
fuente
2
# 2 y # 3 son responsables, pero "¿Cómo funciona el motor Chrome V8?" sin ninguna calificación es demasiado amplia; La única respuesta correcta es un enlace al código fuente V8. ¿Querías preguntar algo más específico sobre V8? (si no, sería mejor eliminar esa parte de la pregunta)
Ixrec
En una segunda mirada, el único punto de mi pregunta # 1 fue entender # 2, por lo que lo eliminaré. Gracias por el aporte.
Anton Paras
Esto no se menciona en las otras respuestas, pero la compilación JIT es difícil. No es algo simple de hacer porque los errores resultantes de la compilación JIT resultan en segfaults en lugar de errores: el programa se bloquea en lugar de arrojar un error en la consola. Sí, para un programador de C competente y cómodo con gdb, esto no es un problema. Pero a casi todos los programadores de C competentes que se sienten cómodos con gdb se les paga por trabajar en otros proyectos. Algunos otros idiomas como Perl y Ruby todavía no tienen intérpretes JIT convencionales.
slebetman
En caso de que te lo estés preguntando. Estoy hablando de esto desde la perspectiva de un desarrollador / mantenedor principal para un lenguaje de programación. Durante un par de años fui contratado para mantener el lenguaje de programación Ferite. Una de las listas de deseos que teníamos era implementar un JIT. Nunca sucedió, nos mudamos para ir en su lugar. PHP solo recientemente obtuvo un compilador JIT (HVVM) gracias a que Facebook invirtió suficiente dinero para hacerlo realidad.
slebetman

Respuestas:

43

La respuesta breve es que JIT tiene tiempos de inicialización más largos, pero es mucho más rápido a largo plazo, y JavaScript originalmente no estaba destinado a largo plazo.

En los años 90, JavaScript típico en un sitio web equivaldría a una o dos funciones en el encabezado, y un puñado de código incrustado directamente en onclickpropiedades y similares. Por lo general, se ejecuta correctamente cuando el usuario esperaba un gran retraso de carga de página de todos modos. Piense en una validación de formularios extremadamente básica o pequeñas utilidades matemáticas como calculadoras de intereses hipotecarios.

Interpretar según sea necesario fue mucho más simple y proporcionó un rendimiento perfectamente adecuado para los casos de uso del día. Si quería algo con rendimiento a largo plazo, usó flash o un applet de Java.

Google Maps en 2004 fue una de las primeras aplicaciones asesinas para el uso intensivo de JavaScript. Abrió los ojos a las posibilidades de JavaScript, pero también destacó sus problemas de rendimiento. Google pasó algún tiempo tratando de alentar a los navegadores a mejorar su rendimiento de JavaScript, luego finalmente decidió que la competencia sería el mejor motivador y también les daría el mejor asiento en la tabla de estándares del navegador. Chrome y V8 fueron lanzados en 2008 como resultado. Ahora, 11 años después de que Google Maps apareció, tenemos nuevos desarrolladores que no recuerdan que JavaScript alguna vez se consideró inadecuado para ese tipo de tarea.

Digamos que tienes una función animateDraggedMap. Puede tomar 500 ms para interpretarlo y 700 ms para JIT compilarlo. Sin embargo, después de la compilación JIT, puede tomar solo 100 ms para ejecutarse realmente. Si son los 90 y solo estás llamando a una función una vez y luego vuelves a cargar la página, JIT no vale la pena. Si es hoy y está llamando animateDraggedMapcientos o miles de veces, esos 200 ms adicionales en la inicialización no son nada, y se puede hacer detrás de escena antes de que el usuario incluso intente arrastrar el mapa.

Karl Bielefeldt
fuente
2

Con la comprensión de lo que está sucediendo en el tiempo de ejecución, es posible realizar cambios en el código o la interpretación del código que le permita ejecutarse más rápido o compilarse mejor de lo que se conoce en el tiempo de compilación anticipado.

Se puede decir bastante sobre esto: es el tema de grandes cantidades de investigación. Mi propia explicación aquí es que comencé a escribir pálidos en comparación con la respuesta dada en Comprender las diferencias: intérprete tradicional, compilador JIT, intérprete JIT y compilador AOT


En pocas palabras, JavaScript no se compiló inicialmente ni se analizó para JIT porque nunca tuvo la intención de ser algo tan complejo o importante.

La intención original de Java Script era vincular a los applets de Java en una página web. La capacidad de hacer clic en algún botón o ingresar un valor en un campo de formulario y luego trabajar en un método de applet de Java se puede ver en Invocar métodos de applet a partir del código JavaScript . También fue posible, a través de JavaScript, ir al otro lado de invocar el código JavaScript desde un applet .

La intención original de JavaScript era vincular los applets y las páginas html que los contenían. Para una tarea tan pequeña, uno no necesita un gran rendimiento (si desea rendimiento, invoque el método de applet que está JIT).

Fue solo después de que Netscape comenzó a hacer un trabajo significativo con JavaScript como su propio lenguaje y lo promovió para el desarrollo (incluido el JavaScript del lado del servidor en el servidor de Netscape Enterprise, que, por cierto, compiló antes de tiempo) que JavaScript salió a la luz como un objetivo serio . Tomó muchos años después de eso las herramientas necesarias para que sea útil.

Comunidad
fuente
1
No, Javascript no está relacionado con Java. Y los applets de Java son JVM bytecode.
Basile Starynkevitch
@BasileStarynkevitch JavaScript fue diseñado para funcionar con applets de Java en páginas de aldea, actuando como el pegamento entre el dom html y los métodos contenidos en los objetos Java. No es y nunca fue destinado a ser Java.
Originalmente, JavaScript se llamaba ECMAScript (o algo así) y no tenía nada que ver con Java. Cómo llegó a llamarse JavaScript es objeto de una investigación separada para los interesados. Esto ha causado confusión desde siempre.
rapid_now
1
@quickly_now y todavía es tc39.github.io/ecma262
caub
Si. ¡Y por alguna extraña razón cuando señalé eso arriba, me votaron por ello!
rapid_now
1

Los JIT son rápidos para JavaScript, porque es imposible generar un código de máquina rápido cuando no conoce el tipo de sus variables.

Cuando no tiene información de tipo, los cálculos son caros. Por ejemplo,

x + y

es bastante complicado si no sabes nada sobre x e y. Pueden ser enteros, dobles, cadenas o incluso objetos en los que este cálculo tiene efectos secundarios. Como no tenemos escritura estática, este es un cálculo costoso.

Con la compilación justo a tiempo, podemos usar información de tiempo de ejecución y convertir esto en un cálculo más rápido. En tiempo de ejecución, V8 realiza un seguimiento del tipo de variables. Si el código anterior se ejecuta varias veces con, digamos, cadenas, el compilador puede ejecutar las instrucciones mucho más simples para la concatenación de cadenas. Entonces, cuando el compilador alcanza x + y, en lugar de ejecutar mucho código que se bifurca para muchos tipos diferentes de x e y, el compilador comprueba rápidamente si tenemos cadenas nuevamente, y luego ejecuta solo unas pocas líneas de código de máquina que concatenan específicamente cadenas.

Por ejemplo, en C ++, el compilador conoce los tipos de xey antes de tiempo, ya que tuvimos que declarar las variables. Por lo tanto, puede generar código de máquina optimizado para concatenar cadenas antes de ejecutar el código.

usuario835611
fuente
0

1) ¿Cómo puede ser más rápido que la interpretación estándar? Bueno, un ejemplo pensado sería el siguiente; supongamos que tenemos 2 aplicaciones ApplicationCompiled y ApplicationInterpreted. Ambos programas hacen exactamente lo mismo y comparten el mismo código fuente. ApplicationCompiled tarda 6 segundos en compilarse.

Digamos que los tiempos del Escenario A son:

  • Para aplicación compilada: 4 segundos
  • Para aplicación interpretada: 12 segundos

Entonces, en total, ApplicationCompiled tarda 10 segundos en ejecutar el Escenario A (compilación de 6 segundos, 4 segundos en ejecución) y ApplicationInterpreted tarda 12 segundos en total en ejecutarse. No tengo un ejemplo específico para mostrarte, y no estoy seguro de en qué casos lo anterior sería cierto; también depende en gran medida de cuán inteligentes sean la interpretación y el compilador.

Obviamente, esto está muy simplificado, pero en mi opinión, la misma idea se puede aplicar a la compilación / interpretación de JIT. La siguiente pregunta sería "¿cómo determinamos, con bajo costo, si esta rama debe compilarse o interpretarse JIT"? Estoy fuera de mi liga aquí :)

2) ¿Por qué no se utilizó JIT-Compilation en primer lugar? No lo sé, pero reconozco que es simplemente una cuestión de recursos y madurez del progreso disponible para hacer que un lenguaje difícil de optimizar como JavaScript aplique técnicas avanzadas como estas. Probablemente había muchas frutas colgando en ese momento.

cwap
fuente