Celebrando 10 años de V8
Este mes marca el décimo aniversario del lanzamiento no solo de Google Chrome, sino también del proyecto V8. Esta publicación ofrece un resumen de los hitos principales del proyecto V8 en los últimos 10 años, así como los años anteriores, cuando el proyecto aún era secreto.
gource
.Antes del lanzamiento de V8: los primeros años
Google contrató a Lars Bak en el otoño de 2006 para construir un nuevo motor de JavaScript para el navegador web Chrome, que en ese momento aún era un proyecto interno secreto de Google. Lars se había mudado recientemente de regreso a Aarhus, Dinamarca, desde Silicon Valley. Como no había una oficina de Google allí y Lars quería permanecer en Dinamarca, Lars y varios de los ingenieros originales del proyecto comenzaron a trabajar en el proyecto en un anexo de su granja. El nuevo entorno de ejecución de JavaScript fue bautizado como “V8”, una referencia juguetona al potente motor que puedes encontrar en un clásico coche muscle. Más tarde, cuando el equipo de V8 había crecido, los desarrolladores se mudaron de sus modestas dependencias a un moderno edificio de oficinas en Aarhus, pero el equipo se llevó consigo su singular impulso y enfoque en construir el entorno de ejecución de JavaScript más rápido del planeta.
Lanzando y evolucionando V8
V8 se convirtió en código abierto el mismo día que se lanzó Chrome: el 2 de septiembre de 2008. El compromiso inicial se remonta al 30 de junio de 2008. Antes de esa fecha, el desarrollo de V8 tuvo lugar en un repositorio privado de CVS. Inicialmente, V8 solo admitía los conjuntos de instrucciones ia32 y ARM y utilizaba SCons como su sistema de compilación.
2009 vio la introducción de un nuevo motor de expresiones regulares llamado Irregexp, lo que resultó en mejoras de rendimiento para las expresiones regulares del mundo real. Con la introducción de un puerto x64, el número de conjuntos de instrucciones compatibles aumentó de dos a tres. 2009 también marcó el primer lanzamiento del proyecto Node.js, que integra V8. La posibilidad de que proyectos no relacionados con navegadores integraran V8 fue mencionada explícitamente en el cómic original de Chrome. ¡Con Node.js, esto realmente sucedió! Node.js creció hasta convertirse en uno de los ecosistemas de JavaScript más populares.
2010 fue testigo de un gran aumento en el rendimiento del entorno de ejecución cuando V8 introdujo un nuevo compilador JIT optimizador. Crankshaft generó código máquina que era dos veces más rápido y un 30% más pequeño que el compilador V8 anterior (sin nombre). Ese mismo año, V8 agregó su cuarto conjunto de instrucciones: MIPS de 32 bits.
En 2011, la recolección de basura mejoró notablemente. Un nuevo recolector de basura incremental redujo drásticamente los tiempos de pausa mientras mantenía un gran rendimiento máximo y bajo uso de memoria. V8 introdujo el concepto de Isolates, lo cual permite a los integradores iniciar múltiples instancias del entorno de ejecución de V8 en un proceso, allanando el camino para trabajadores web más ligeros en Chrome. La primera de las dos migraciones del sistema de compilación de V8 ocurrió durante la transición de SCons a GYP. Implementamos soporte para ES5 en modo estricto. Mientras tanto, el desarrollo se trasladó de Aarhus a Múnich (Alemania) bajo un nuevo liderazgo, con mucha polinización cruzada con el equipo original en Aarhus.
2012 fue un año de hitos para el proyecto V8. El equipo hizo esfuerzos de optimización para mejorar el rendimiento de V8 medido a través de las suites de benchmarks SunSpider y Kraken. Más tarde, desarrollamos una nueva suite de benchmarks llamada Octane (con V8 Bench como núcleo) que puso la competencia de rendimiento máximo en primer plano y estimuló mejoras masivas en la tecnología de tiempo de ejecución y JIT en todos los principales motores de JS. Uno de los resultados de estos esfuerzos fue el cambio de muestreo aleatorio a una técnica determinista basada en conteo para detectar funciones “calientes” en el perfilador de tiempo de ejecución de V8. Esto hizo que fuera significativamente menos probable que algunas cargas de página (o ejecuciones de benchmarks) fueran aleatoriamente mucho más lentas que otras.
2013 fue testigo de la aparición de un subconjunto de bajo nivel de JavaScript llamado asm.js. Dado que asm.js está limitado a aritmética estáticamente tipada, llamadas a funciones y accesos a pilas con tipos primitivos únicamente, el código asm.js validado podía ejecutarse con rendimiento predecible. Publicamos una nueva versión de Octane, Octane 2.0 con actualizaciones a los benchmarks existentes, así como nuevos benchmarks orientados a casos de uso como asm.js. Octane estimuló el desarrollo de nuevas optimizaciones del compilador como allocation folding y optimizaciones basadas en sitios de asignación para transiciones de tipo y pretenuring que mejoraron significativamente el rendimiento máximo. Como parte de un esfuerzo que apodamos internamente “Handlepocalypse”, la API de Handle de V8 fue completamente reescrita para hacerla más fácil de usar correctamente y de manera segura. También en 2013, la implementación de TypedArray
s de Chrome en JavaScript fue movida de Blink a V8.
En 2014, V8 trasladó parte del trabajo de compilación JIT fuera del hilo principal con compilación concurrente, reduciendo el bloqueo y mejorando significativamente el rendimiento. Más tarde ese año, introdujimos landed la versión inicial de un nuevo compilador optimizador llamado TurboFan. Mientras tanto, nuestros socios ayudaron a portar V8 a tres nuevas arquitecturas de conjunto de instrucciones: PPC, MIPS64 y ARM64. Siguiendo a Chromium, V8 pasó a otro sistema de compilación, GN. La infraestructura de pruebas de V8 vio mejoras significativas, con un Tryserver ahora disponible para probar cada parche en varios bots de compilación antes de ser introducido. Para el control de fuente, V8 migró de SVN a Git.
2015 fue un año ocupado para V8 en varios frentes. Implementamos almacenamiento en caché de código y streaming de scripts, acelerando significativamente los tiempos de carga de las páginas web. El trabajo en el uso de mementos de asignación de nuestro sistema de tiempo de ejecución fue publicado en ISMM 2015. Más tarde ese año, iniciamos el trabajo en un nuevo intérprete llamado Ignition. Experimentamos con la idea de subestablecer JavaScript con modo fuerte para lograr garantías más fuertes y un rendimiento más predecible. Implementamos el modo fuerte detrás de una bandera, pero más tarde encontramos que sus beneficios no justificaban los costos. La adición de una cola de commits mejoró significativamente la productividad y la estabilidad. El recolector de basura de V8 también comenzó a cooperar con integradores como Blink para programar trabajos de recolección de basura durante períodos de inactividad. La recolección de basura en tiempos de inactividad redujo significativamente el bloqueo observable de la recolección de basura y el consumo de memoria. En diciembre, el primer prototipo de WebAssembly fue introducido en V8.
En 2016, enviamos las últimas piezas del conjunto de características ES2015 (anteriormente conocido como "ES6") (incluyendo promesas, sintaxis de clases, alcance léxico, desestructuración y más), así como algunas características de ES2016. También comenzamos a implementar la nueva canalización Ignition y TurboFan, usándola para compilar y optimizar características de ES2015 y ES2016 y enviando Ignition por defecto para dispositivos Android de gama baja. Nuestro exitoso trabajo en recolección de basura en tiempo de inactividad fue presentado en PLDI 2016. Iniciamos el proyecto Orinoco, un nuevo recolector de basura mayormente paralelo y concurrente para V8 para reducir el tiempo de recolección de basura en el hilo principal. En un cambio importante, orientamos nuestros esfuerzos de rendimiento lejos de los micro-benchmarks sintéticos y comenzamos a medir y optimizar seriamente el rendimiento en el mundo real. Para depuración, el inspector de V8 fue migrado de Chromium a V8, permitiendo que cualquier incorporador de V8 (y no solo Chromium) use las herramientas de desarrollo de Chrome para depurar JavaScript corriendo en V8. El prototipo de WebAssembly pasó de ser un prototipo a soporte experimental, en coordinación con otros proveedores de navegadores soporte experimental para WebAssembly. V8 recibió el Premio de Software de Lenguajes de Programación de ACM SIGPLAN. Y se agregó otro puerto: S390.
En 2017, finalmente completamos nuestra renovación de varios años del motor, habilitando por defecto la nueva canalización Ignition y TurboFan. Esto hizo posible eliminar más tarde Crankshaft (130,380 líneas de código eliminadas) y Full-codegen de la base de código. Lanzamos Orinoco v1.0, incluyendo marcado concurrente, barrido concurrente, limpieza paralela y compactación paralela. Reconocimos oficialmente a Node.js como un incorporador de V8 de primera clase junto a Chromium. Desde entonces, es imposible que un parche de V8 se implemente si hacerlo rompe la suite de pruebas de Node.js. Nuestra infraestructura ganó soporte para fuzzing de corrección, asegurando que cualquier pieza de código produzca resultados consistentes independientemente de la configuración en que se ejecute.
En un lanzamiento coordinado a nivel de la industria, V8 envió WebAssembly activado por defecto. Implementamos soporte para módulos JavaScript así como para los conjuntos de características completas de ES2017 y ES2018 (incluyendo funciones asíncronas, memoria compartida, iteración asíncrona, propiedades rest/spread y las características de RegExp). Enviamos soporte nativo para cobertura de código JavaScript y lanzamos el Benchmark de Herramientas Web para ayudarnos a medir cómo las optimizaciones de V8 impactan el rendimiento para herramientas de desarrollo del mundo real y el código JavaScript que generan. El seguimiento de envolturas de objetos JavaScript a objetos DOM de C++ y viceversa nos permitió resolver fugas de memoria de larga data en Chrome y manejar el cierre transitivo de objetos sobre el montón de JavaScript y Blink de manera eficiente. Más tarde utilizamos esta infraestructura para aumentar las capacidades de la herramienta de desarrollo de snapshotting del montón.
2018 trajo un evento de seguridad a nivel de la industria que desestabilizó lo que creíamos saber sobre la seguridad de la información en CPU con la divulgación pública de las vulnerabilidades Spectre/Meltdown. Los ingenieros de V8 realizaron extensas investigaciones ofensivas para ayudar a comprender la amenaza para lenguajes gestionados y desarrollar mitigaciones. V8 envió mitigaciones contra Spectre y ataques similares de canal lateral para los incorporadores que ejecutan código no confiable.
Recientemente, enviamos un compilador básico para WebAssembly llamado Liftoff que reduce significativamente el tiempo de inicio para aplicaciones de WebAssembly mientras aún logra un rendimiento predecible. Enviamos BigInt
, un nuevo primitivo de JavaScript que permite enteros de precisión arbitraria. Implementamos funciones integradas embebidas, y las hicimos posibles de deserializar perezosamente, reduciendo significativamente el tamaño de V8 para múltiples Isolates. Hicimos posible compilar bytecode de scripts en un hilo de fondo. Comenzamos el proyecto del montón unificado V8-Blink para ejecutar una recolección de basura sincronizada para V8 y Blink en los componentes cruzados. Y el año aún no ha terminado...
Altibajos de rendimiento
La puntuación de V8 Bench de Chrome a lo largo de los años muestra el impacto en el rendimiento de los cambios de V8. (Estamos utilizando el V8 Bench porque es uno de los pocos benchmarks que aún puede ejecutarse en la beta original de Chrome.)
¡Nuestra puntuación en este benchmark aumentó 4× en los últimos diez años!
Sin embargo, podrías notar dos caídas en el rendimiento a lo largo de los años. Ambas son interesantes porque corresponden a eventos significativos en la historia de V8. La caída de rendimiento en 2015 ocurrió cuando V8 envió versiones iniciales de las características de ES2015. Estas características eran de amplio alcance en la base de código de V8, y por lo tanto nos enfocamos en la corrección en lugar del rendimiento para su lanzamiento inicial. Aceptamos estas pequeñas regresiones de velocidad para llevar las características a los desarrolladores lo más rápido posible. A principios de 2018, se divulgó la vulnerabilidad Spectre, y V8 envió mitigaciones para proteger a los usuarios contra posibles ataques, lo que resultó en otra regresión en el rendimiento. Por suerte, ahora que Chrome está enviando aislamiento de sitios, podemos desactivar las mitigaciones nuevamente, llevando el rendimiento a niveles anteriores.
Otro punto destacado de este gráfico es que comienza a estabilizarse alrededor de 2013. ¿Significa eso que V8 se rindió y dejó de invertir en rendimiento? ¡Todo lo contrario! El aplanamiento de los gráficos representa el cambio del equipo de V8 de micro-benchmarks sintéticos (como V8 Bench y Octane) a optimizar para rendimiento en el mundo real. V8 Bench es un antiguo benchmark que no utiliza ninguna característica moderna de JavaScript, ni tampoco se asemeja al código de producción real del mundo real. Contrasta esto con la suite de benchmarks Speedometer más reciente:
Aunque V8 Bench muestra mejoras mínimas de 2013 a 2018, nuestra puntuación en Speedometer 1 aumentó (otra vez) 4× en este mismo período. (Usamos Speedometer 1 porque Speedometer 2 utiliza características modernas de JavaScript que aún no estaban soportadas en 2013).
Hoy en día, tenemos incluso mejores benchmarks que reflejan con mayor precisión las aplicaciones modernas de JavaScript y, además, medimos y optimizamos activamente las aplicaciones web existentes.
Resumen
Aunque V8 fue construido originalmente para Google Chrome, siempre ha sido un proyecto independiente con una base de código separada y una API de integración que permite que cualquier programa utilice sus servicios de ejecución de JavaScript. En los últimos 10 años, la naturaleza abierta del proyecto lo ha ayudado a convertirse en una tecnología clave no solo para la Plataforma Web, sino también en otros contextos como Node.js. A lo largo del camino, el proyecto evolucionó y permaneció relevante a pesar de muchos cambios y un crecimiento dramático.
Inicialmente, V8 solo soportaba dos conjuntos de instrucciones. En los últimos 10 años, la lista de plataformas soportadas alcanzó ocho: ia32, x64, ARM, ARM64, MIPS de 32 y 64 bits, PPC de 64 bits y S390. El sistema de construcción de V8 migró de SCons a GYP y luego a GN. El proyecto se trasladó de Dinamarca a Alemania, y ahora cuenta con ingenieros en todo el mundo, incluidos Londres, Mountain View y San Francisco, con colaboradores fuera de Google provenientes de muchos más lugares. Hemos transformado toda nuestra canalización de compilación de JavaScript desde componentes sin nombre hasta Full-codegen (un compilador base) y Crankshaft (un compilador optimizador basado en retroalimentación) hasta Ignition (un intérprete) y TurboFan (un mejor compilador optimizador basado en retroalimentación). V8 pasó de ser "solo" un motor de JavaScript a también soportar WebAssembly. El lenguaje JavaScript en sí mismo evolucionó de ECMAScript 3 a ES2018; la última versión de V8 incluso implementa características posteriores a ES2018.
El arco narrativo de la Web es largo y duradero. Celebrar el décimo aniversario de Chrome y V8 es una buena oportunidad para reflexionar que, aunque es un gran hito, la narrativa de la Plataforma Web ha durado más de 25 años. No tenemos dudas de que la historia de la Web continuará al menos tanto tiempo en el futuro. Estamos comprometidos a garantizar que V8, JavaScript y WebAssembly sigan siendo personajes interesantes en esa narrativa. Estamos emocionados de ver qué nos depara la próxima década. ¡Mantente atento!