Es posible que hayas oído hablar de Project Quantum … es una reescritura importante de las partes internas de Firefox para hacer que Firefox sea rápido. Estamos intercambiando partes de nuestro buscador experimental, Servo, y realizando mejoras masivas en otras partes del motor.

El proyecto ha sido comparado con el reemplazo de un motor a reacción mientras el avión aún está en vuelo. Estamos implementando los cambios, componente por componente, para que pueda ver los efectos en Firefox tan pronto como cada componente esté listo.

Y el primer componente principal de Servo, un nuevo motor de CSS llamado Quantum CSS (anteriormente conocido como Stylo), ahora está disponible para probar en nuestra versión Nightly. Puede asegurarse de que esté activada yendo about:configay estableciendo layout.css.servo.enableden verdadero.

Este nuevo motor reúne innovaciones de vanguardia de cuatro navegadores diferentes para crear un nuevo motor supercss.

4 motores de búsqueda que se introducen en Quantum CSS

Aprovecha hardware moderno, paralelizando el trabajo en todos los núcleos de su máquina. Esto significa que puede funcionar hasta 2 o 4 o incluso 18 veces más rápido.

Además de eso, combina optimizaciones de vanguardia existentes de otros navegadores. Por lo tanto, incluso si no se ejecutara en paralelo, seguiría siendo un motor de CSS rápido.

Jets de carreras

Pero, ¿qué hace el motor de CSS? Primero veamos el motor de CSS y cómo encaja en el resto del navegador. Entonces podemos ver cómo Quantum CSS lo hace todo más rápido.

¿Qué hace el motor de CSS?

El motor CSS es parte del motor de renderizado del navegador. El motor de representación toma los archivos HTML y CSS del sitio web y los convierte en píxeles en la pantalla.

Archivos a píxeles

Cada navegador tiene un motor de renderizado. En Chrome, se llama Blink. En Edge, se llama EdgeHTML. En Safari, se llama WebKit. Y en Firefox, se llama Gecko.

Para pasar de archivos a píxeles, todos estos motores de renderizado básicamente hacen las mismas cosas:

  1. Analice los archivos en objetos que el navegador pueda comprender, incluido el DOM. En este punto, el DOM conoce la estructura de la página. Conoce las relaciones entre padres e hijos entre los elementos. Sin embargo, no sabe cómo deberían verse esos elementos.Analizando el HTML en un árbol DOM
  2. Averigua cómo deberían ser los elementos. Para cada nodo DOM, el motor CSS averigua qué reglas de CSS se aplican. Luego calcula valores para cada propiedad CSS para ese nodo DOM.Diseñando cada nodo DOM en el árbol adjuntando estilos calculados
  3. Calcule las dimensiones de cada nodo y hacia dónde va en la pantalla. Los cuadros se crean para cada cosa que aparecerá en la pantalla. Los recuadros no solo representan nodos DOM … también tendrá cuadros para cosas dentro de los nodos DOM, como líneas de texto.Medir todos los cuadros para crear un árbol de marco
  4. Pintar las diferentes cajas. Esto puede suceder en múltiples capas. Pienso en esto como la animación dibujada a mano de antaño, con capas de papel de cebolla. Eso hace que sea posible cambiar una capa sin tener que volver a pintar cosas en otras capas.Capas de pintura
  5. Tome esas diferentes capas pintadas, aplique las propiedades de solo compositor como transformaciones y conviértalas en una imagen. Esto es básicamente como tomar una foto de las capas apiladas juntas. Esta imagen se representará en la pantalla.Ensamblar las capas juntas y tomar una foto

Esto significa que cuando comienza a calcular los estilos, el motor de CSS tiene dos cosas:

  • un árbol DOM
  • una lista de reglas de estilo

Pasa por cada nodo DOM, uno por uno, y calcula los estilos para ese nodo DOM. Como parte de esto, le da al nodo DOM un valor para cada propiedad de CSS, incluso si las hojas de estilo no declaran un valor para esa propiedad.

Creo que es como alguien que está pasando y completando un formulario. Deben completar uno de estos formularios para cada nodo DOM. Y para cada campo de formulario, necesitan una respuesta.

Formulario en blanco con propiedades de CSS

Para hacer esto, el motor CSS necesita hacer dos cosas:

  • averiguar qué reglas se aplican al nodo – también conocido como selector de coincidencia
  • complete cualquier valor que falte con los valores del padre o un valor predeterminado, también conocido como la cascada

Selector de Selector

Para este paso, agregaremos cualquier regla que coincida con el nodo DOM a una lista. Debido a que varias reglas pueden coincidir, puede haber múltiples declaraciones para la misma propiedad.

Persona que pone marcas de verificación junto a las reglas CSS coincidentes

Además, el navegador en sí mismo agrega algunas CSS predeterminadas (llamadas hojas de estilo de agente de usuario). ¿Cómo sabe el motor de CSS qué valor elegir?

Aquí es donde entran las reglas de especificidad. El motor de CSS básicamente crea una hoja de cálculo. Luego ordena las declaraciones basadas en diferentes columnas.

Declaraciones en una hoja de cálculo

La regla que tiene la especificidad más alta gana. Por lo tanto, en función de esta hoja de cálculo, el motor de CSS llena los valores que puede.

Formulario con algunas propiedades de CSS completadas

Por lo demás, usaremos la cascada.

La cascada

La cascada hace que CSS sea más fácil de escribir y mantener. Debido a la cascada, puede establecer la colorpropiedad en el cuerpo y saber que el texto en p, y span, y los lielementos usarán ese color (a menos que tenga una anulación más específica).

Para hacer esto, el motor de CSS mira los cuadros en blanco en su formulario. Si la propiedad hereda de manera predeterminada, el motor de CSS camina hacia el árbol para ver si uno de los antepasados ​​tiene un valor. Si ninguno de los antepasados ​​tiene un valor, o si la propiedad no hereda, obtendrá un valor predeterminado.

Formulario completará todas las propiedades de CSS

Entonces ahora todos los estilos han sido calculados para este nodo DOM.

Una nota al margen: compartir estructuras de estilo

La forma que te he estado mostrando es un poco tergiversada. CSS tiene cientos de propiedades. Si el motor de CSS retenía un valor para cada propiedad para cada nodo DOM, pronto se quedaría sin memoria.

En cambio, los motores usualmente hacen algo llamado intercambio de estructuras de estilo. Almacenan datos que generalmente van juntos (como propiedades de fuente) en un objeto diferente llamado estructura de estilo. Entonces, en lugar de tener todas las propiedades en el mismo objeto, el objeto de estilos calculado solo tiene punteros. Para cada categoría, hay un puntero a la estructura de estilo que tiene los valores correctos para este nodo DOM.

Trozos de la forma extraídos para separar objetos

Esto termina ahorrando memoria y tiempo. Los nodos que tienen propiedades similares (como los hermanos) pueden apuntar a las mismas estructuras para las propiedades que comparten. Y debido a que muchas propiedades se heredan, un ancestro puede compartir una estructura con cualquier descendiente que no especifique sus propias anulaciones.

Ahora, ¿cómo lo hacemos rápido?

Entonces, así es como se ve el cálculo del estilo cuando no lo has optimizado.

Pasos en el cálculo del estilo CSS: coincidencia de selector, clasificación por especificidad y cálculo de valores de propiedad

Aquí hay mucho trabajo sucediendo. Y no solo tiene que suceder en la carga de la primera página. Ocurre una y otra vez a medida que los usuarios interactúan con la página, pasando el ratón sobre los elementos o realizando cambios en el DOM, lo que desencadena un restyle.

Estilo inicial más restyling para hover, nodos DOM agregados, etc.

Esto significa que el cálculo del estilo CSS es un gran candidato para la optimización … y los navegadores han estado probando diferentes estrategias para optimizarlo durante los últimos 20 años. Lo que hace Quantum CSS es tomar lo mejor de estas estrategias de diferentes motores y combinarlas para crear un motor nuevo y superrápido.

Así que veamos los detalles de cómo funcionan todos juntos.

Ejecutar todo en paralelo

El proyecto Servo (del que proviene Quantum CSS) es un navegador experimental que intenta poner en paralelo todas las partes diferentes de la representación de una página web. Qué significa eso?

Una computadora es como un cerebro. Hay una parte que hace el pensamiento (la ALU). Cerca de eso, hay algo de memoria a corto plazo (los registros). Estos se agrupan en la CPU. Luego hay memoria a más largo plazo, que es RAM.

CPU con ALU (la parte que hace el pensamiento) y registros (memoria a corto plazo)

Las primeras computadoras solo podían pensar una cosa a la vez usando esta CPU. Pero en la última década, las CPU se han desplazado a tener múltiples ALU y registros, agrupados en núcleos. Esto significa que la CPU puede pensar varias cosas a la vez, en paralelo.

Chip de CPU con múltiples núcleos que contienen ALU y registros

Quantum CSS hace uso de esta característica reciente de las computadoras al dividir el cálculo del estilo para los diferentes nodos DOM en los diferentes núcleos.

Esto puede parecer una cosa fácil de hacer … simplemente separe las ramas del árbol y hágalo en diferentes núcleos. En realidad es mucho más difícil por algunas razones. Una razón es que los árboles DOM a menudo son desiguales. Eso significa que un núcleo tendrá mucho más trabajo que hacer que otros.

El árbol DOM desbalanceado se divide entre múltiples núcleos, por lo que uno hace todo el trabajo


Para equilibrar el trabajo de manera más uniforme, Quantum CSS utiliza una técnica llamada robo de trabajo. Cuando se procesa un nodo DOM, el código toma sus hijos directos y los divide en 1 o más «unidades de trabajo». Estas unidades de trabajo se ponen en una cola.

Núcleos segmentando su trabajo en unidades de trabajo

Cuando se realiza un núcleo con el trabajo en cola, puede buscar en las otras colas para encontrar más trabajo por hacer. Esto significa que podemos dividir el trabajo de manera pareja sin tomar el tiempo adelantado para caminar por el árbol y descubrir cómo equilibrarlo antes de tiempo.

Núcleos que han terminado su trabajo robando desde el núcleo con más trabajo

En la mayoría de los navegadores, sería difícil hacer esto bien. El paralelismo es un problema difícil conocido, y el motor de CSS es muy complejo. También se encuentra entre las otras dos partes más complejas del motor de renderizado: el DOM y el diseño. Por lo tanto, sería fácil introducir un error, y el paralelismo puede dar como resultado errores que son muy difíciles de rastrear, llamados carreras de datos. Explico más sobre este tipo de errores en otro artículo .

Si acepta contribuciones de cientos o miles de ingenieros, ¿cómo puede programar en paralelo sin temor? Para eso tenemos a Rust.

Logotipo de Rust

Con Rust, puedes verificar estáticamente que no tienes carreras de datos. Esto significa que evita los errores difíciles de depurar simplemente al no dejarlos entrar en su código en primer lugar. El compilador no te dejará hacerlo. Escribiré más sobre esto en un artículo futuro. Mientras tanto, puedes ver este video introductorio sobre el paralelismo en Rust o esta charla más profunda sobre el robo de trabajo .

Con esto, el cálculo del estilo CSS se convierte en lo que se llama un problema embarazosamente paralelo: hay muy poco que le impida ejecutarlo eficientemente en paralelo. Esto significa que podemos acercarnos a las aceleraciones lineales. Si tiene 4 núcleos en su máquina, se ejecutará cerca de 4 veces más rápido.

Acelera los restyles con el Árbol de reglas

Para cada nodo DOM, el motor CSS necesita pasar por todas las reglas para hacer la coincidencia de selector. Para la mayoría de los nodos, esta coincidencia probablemente no cambiará muy a menudo. Por ejemplo, si el usuario pasa el mouse sobre un elemento primario, las reglas que coinciden pueden cambiar. Todavía necesitamos recalcular el estilo para que sus descendientes administren la herencia de propiedades, pero las reglas que coinciden con esos descendientes probablemente no cambien.

Sería bueno si pudiéramos simplemente anotar qué reglas coinciden con esos descendientes, así no tenemos que volver a hacer la coincidencia de selectores para ellos … y eso es lo que hace el árbol de reglas: tomado prestado del anterior motor de CSS de Firefox.

El motor de CSS pasará por el proceso de descifrar los selectores que coinciden y luego ordenarlos por especificidad. A partir de esto, crea una lista vinculada de reglas.

Esta lista se agregará al árbol.

Una lista vinculada de reglas que se agregan al árbol de reglas

El motor de CSS intenta mantener el número de ramas en el árbol al mínimo. Para hacer esto, intentará reutilizar una rama donde sea que pueda.

Si la mayoría de los selectores de la lista son los mismos que una rama existente, seguirá la misma ruta. Pero podría llegar a un punto en el que la siguiente regla en la lista no esté en esta rama del árbol. Solo en ese punto agregará una nueva rama.

El último elemento de la lista vinculada que se agrega al árbol

El nodo DOM obtendrá un puntero a la regla que se insertó al último (en este ejemplo, la div#warningregla). Este es el más específico.

En restyle, el motor hace una comprobación rápida para ver si el cambio al padre podría cambiar las reglas que coinciden con los niños. De lo contrario, para cualquier descendiente, el motor solo puede seguir el puntero en el nodo descendiente para llegar a esa regla. A partir de ahí, puede seguir el árbol hasta la raíz para obtener la lista completa de reglas de coincidencia, desde la más específica a la menos específica. Esto significa que puede omitir el selector y la ordenación por completo.

Saltar selector de selección y clasificación por especificidad

Esto ayuda a reducir el trabajo necesario durante el restyle. Pero sigue siendo mucho trabajo durante el diseño inicial. Si tiene 10,000 nodos, aún necesita hacer un selector que coincida 10,000 veces. Pero hay otra forma de acelerar eso.

Acelera el procesamiento inicial (y la cascada) con la memoria caché de intercambio de estilos

Piensa en una página con miles de nodos. Muchos de esos nodos coincidirán con las mismas reglas. Por ejemplo, piense en una página de Wikipedia larga … los párrafos en el área de contenido principal deberían todos coincidir exactamente con las mismas reglas, y tener exactamente los mismos estilos computados.

Si no hay optimización, el motor CSS tiene que coincidir con los selectores y calcular los estilos para cada párrafo individualmente. Pero si hubiera una manera de probar que los estilos serán los mismos de un párrafo a otro, el motor podría hacer ese trabajo una vez y apuntar a cada nodo de párrafo al mismo estilo calculado.

Eso es lo que hace el estilo de intercambio de caché, inspirado por Safari y Chrome. Una vez hecho el procesamiento de un nodo, coloca el estilo calculado en la memoria caché. Luego, antes de que comience a calcular estilos en el siguiente nodo, ejecuta algunas comprobaciones para ver si puede usar algo de la memoria caché.

Esos controles son:

  • ¿Los 2 nodos tienen los mismos identificadores, clases, etc.? Si es así, entonces coincidirían con las mismas reglas.
  • Para cualquier cosa que no esté basada en el selector, por ejemplo, los estilos en línea, ¿los nodos tienen los mismos valores? Si es así, entonces las reglas de arriba no serán anuladas, o serán anuladas de la misma manera.
  • ¿Ambos padres apuntan al mismo objeto de estilo calculado? Si es así, los valores heredados también serán los mismos.

Los hermanos comparten los estilos computados y luego preguntan si un primo puede compartir.  Respuesta: sí

Esos controles han estado en el estilo anterior compartiendo cachés desde el principio. Pero hay muchos otros casos en los que los estilos pueden no coincidir. Por ejemplo, si una regla CSS usa el :first-childselector, es posible que dos párrafos no coincidan, aunque las comprobaciones anteriores sugieran que deberían hacerlo.

En WebKit y Blink, el estilo de intercambio de caché se daría por vencido en estos casos y no usaría el caché. A medida que más sitios usan estos selectores modernos, la optimización se volvió cada vez menos útil, por lo que el equipo Blink la eliminó recientemente. Pero resulta que hay una forma de que el estilo de intercambio de caché se mantenga al día con estos cambios.

En Quantum CSS, reunimos todos esos selectores raros y verificamos si se aplican al nodo DOM. Luego almacenamos las respuestas como unos y ceros. Si los dos elementos tienen los mismos y ceros, sabemos que definitivamente coinciden.

Un marcador mostrando 0s y 1s, con las columnas etiquetadas con selectores como: primer hijo

Si un nodo DOM puede compartir estilos que ya se han calculado, puede omitir casi todo el trabajo. Debido a que las páginas a menudo tienen muchos nodos DOM con los mismos estilos, este estilo de intercambio de caché puede ahorrar memoria y también acelerar realmente las cosas.

Saltarse todo el trabajo

Conclusión

Esta es la primera gran transferencia de tecnología de Servo Tech a Firefox. En el camino, hemos aprendido mucho sobre cómo llevar el código moderno y de alto rendimiento escrito en Rust al corazón de Firefox.

Estamos muy contentos de tener esta gran parte de Project Quantum lista para que los usuarios la experimenten de primera mano. Estaremos encantados de que lo pruebes, y avísanos si encuentras algún problema .

Puedes encontrar el articulo original en : https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo/ 

Deja un comentario

Tendencias