Los sitios modernos como Google Docs y Gmail ejecutan tanto JavaScript que la velocidad del motor tenía dos cuellos de botella distintos. El intérprete de bytecode en C++ de Firefox era lento y no recopilaba información de tipos, mientras que el JIT Baseline compilaba cada función a código máquina; en páginas pesadas eso significaba compilar miles de funciones, así que el tiempo se destinaba a compilar en lugar de a ejecutar. El JIT optimizador Ion solo se volvía rápido una vez que el JIT Baseline había reunido datos de tipos.
Las soluciones obvias tenían cada una un costo: acelerar el intérprete en C++ y aun así no le da al optimizador ningún dato de tipos; retrasar o mover la compilación Baseline y las rutas frías o las salidas de emergencia se vuelven riesgosas; reanudar las salidas de emergencia de Ion en medio del código JIT y cada característica necesita metadatos adicionales y código complejo para depuradores y excepciones. Mozilla en cambio añadió un nuevo nivel, el Intérprete Baseline, situado entre el intérprete en C++ y el JIT Baseline.
La decisión que abarató esto no fue escribir un intérprete desde cero. El JIT Baseline es un JIT de plantillas: cada instrucción de bytecode ya se corresponde con una secuencia de código máquina en gran medida fija. Mozilla generó esas mismas secuencias en un bucle de intérprete encadenado, compartiendo un único generador de código con plantillas, el mismo diseño de marco y una única estructura JitScript que contiene las cachés en línea y los datos de perfilado que usan ambos niveles.
Como la interpretación ahora reunía información de tipos, el umbral de calentamiento del JIT Baseline pudo subir de 10 a 100, recortando drásticamente cuántas funciones se compilaban. Firefox Nightly midió cargas de página entre un 2 y un 8 % más rápidas, pruebas de devtools entre un 2 y un 10 % más rápidas, ahorros de memoria y código más simple: las salidas de emergencia de Ion se reanudaban en el intérprete, y buena parte de la complicada maquinaria de manejo de depuradores y excepciones fue eliminada.
Generar el nivel económico a partir del mismo generador de código que el nivel rápido, para que las cachés y los datos de tipos sigan compartiéndose
Reutilizar el generador de código del JIT significa que el comportamiento del intérprete y las cachés en línea coinciden con el JIT por construcción, sin una segunda implementación que mantener sincronizada.
La interpretación ahora reúne los mismos datos de tipos que antes reunía el JIT, así que el nivel optimizador obtiene su perfil sin esperar a la compilación.
Multiplicar por diez el umbral de calentamiento del JIT redujo drásticamente cuántas funciones se compilaban en páginas con mucho JavaScript.
Las salidas de emergencia, el soporte de depuradores y el manejo de excepciones se simplificaron porque Ion se reanuda en el intérprete en lugar de en medio del código JIT, necesitando menos metadatos.
Entrar al nivel intermedio es económico: la asignación de JitScript se ejecuta al menos 10 veces más rápido que la compilación del JIT Baseline.
No escribir a mano el nivel intermedio entre el lento y el rápido: generarlo a partir de las propias plantillas del nivel rápido, y las cachés, los marcos y el comportamiento se mantienen idénticos por construcción
El Intérprete Baseline llegó a Firefox Nightly en julio de 2019 y se dirigía al lanzamiento general de Firefox 70 ese octubre. Las mediciones de Mozilla mostraron cargas de página entre un 2 y un 8 % más rápidas y pruebas de devtools entre un 2 y un 10 % más rápidas, con ahorros de memoria además. La limpieza que permitió también aceleró la compilación del JIT Baseline, y el siguiente paso declarado por el equipo era sacar esa compilación del hilo principal.
CONSULTA LAS PRUEBAS
Las fuentes
- The Baseline Interpreter: a faster JS interpreter in Firefox 70 Mozilla Hacks · hacks.mozilla.org ·
- Hacker News discussion: The Baseline Interpreter: a faster JS interpreter in Firefox 70 Hacker News · news.ycombinator.com ·