Esta publicación es de Ahmed Elwasefi, colaborador de Chromium, quien comparte cómo se convirtió en colaborador a través de Google Summer of Code y los problemas de rendimiento de accesibilidad que identificó y corrigió.
Cuando me acercaba al último año de Ingeniería Informática en la Universidad Alemana de El Cairo, decidí explorar oportunidades para contribuir al código abierto. Empecé a explorar la lista de problemas aptos para principiantes de Chromium y me sentí particularmente atraído por la accesibilidad. Mi búsqueda de orientación me llevó a Aaron leventhal, cuya experiencia y disposición para ayudar me inspiraron a formar equipo con él para un proyecto. Esta colaboración se convirtió en mi experiencia de Google Summer of Code, en la que me aceptaron para trabajar con el equipo de accesibilidad de Chromium.
Después de completar con éxito Google Summer of Code, seguí abordando un problema de desplazamiento sin resolver, impulsado por el deseo de mejorar el rendimiento. Gracias a dos subvenciones del programa OpenCollective de Google, pude seguir trabajando en este proyecto y, al mismo tiempo, asumir tareas adicionales enfocadas en optimizar el código para mejorar el rendimiento.
En esta entrada de blog, comparto mi recorrido con Chromium durante el último año y medio, y detallo las mejoras técnicas que realizamos, en particular en el área del rendimiento.
Cómo el código de accesibilidad afecta el rendimiento en Chrome
El código de accesibilidad de Chrome ayuda a las tecnologías de accesibilidad, como los lectores de pantalla, a acceder a la Web. Sin embargo, cuando se habilita, puede afectar los tiempos de carga, el rendimiento y la duración de batería. Por lo tanto, si no es necesario, este código permanece inactivo para evitar que se ralentice el rendimiento. Aproximadamente entre el 5 y el 10% de los usuarios tienen habilitado el código de accesibilidad, a menudo debido a herramientas como administradores de contraseñas y software antivirus que usan APIs de accesibilidad de la plataforma. Estas herramientas dependen de estas APIs para interactuar con el contenido de la página y modificarlo, como ubicar campos de contraseña para administradores de contraseñas y generadores de formularios.
Aún no se conoce la degradación total en las métricas principales, pero un experimento reciente llamado Inhabilitar accesibilidad automáticamente (desactiva la accesibilidad cuando no se usa) muestra que es bastante alta. El problema se produce debido a la gran cantidad de procesamiento y comunicación en dos áreas principales de la base de código de accesibilidad de Chrome: el renderizador y el navegador. El renderizador recopila información sobre el contenido web y los cambios en el contenido, y calcula las propiedades de accesibilidad para un árbol de nodos. Luego, se serializan los nodos no sincronizados y se envían a través de un canal al subproceso principal de IU del proceso del navegador. Este subproceso recibe y desserializa esta información en el árbol de nodos idéntico y, luego, la convierte en un formato adecuado para tecnologías de accesibilidad de terceros, como los lectores de pantalla.
Mejoras en la accesibilidad de Chromium
Los siguientes proyectos se completaron durante Summer of Code y, luego, se financiaron con el programa Google OpenCollective.
Mejora de caché
En Chrome, hay una estructura de datos especial llamada árbol de accesibilidad que refleja el árbol de DOM. Se usa para ayudar a las tecnologías de accesibilidad a acceder al contenido web. A veces, cuando un dispositivo necesita información de este árbol, es posible que aún no esté lista, por lo que el navegador debe programar esas solicitudes para más adelante.
Anteriormente, esta programación se controlaba con un método llamado cierres, que implicaba colocar devoluciones de llamada en una cola. Este enfoque agregó trabajo adicional debido a la forma en que se procesan los cierres.
Para mejorar esto, cambiamos a un sistema que usa enums. A cada tarea se le asigna un valor de enumeración específico y, una vez que el árbol de accesibilidad está listo, se llama al método correcto para esa tarea. Este cambio hizo que el código fuera más fácil de entender y mejoró el rendimiento en más del 20%.
Cómo encontrar y solucionar problemas de rendimiento del desplazamiento
A continuación, exploré cómo mejora el rendimiento cuando desactivamos la serialización de cajas de límites. Los cuadros de límite son las posiciones y los tamaños de los elementos en una página web, incluidos detalles como el ancho, la altura y su posición en relación con su elemento superior.
Para probar esto, quitamos temporalmente el código que controla los cuadros delimitados y realizamos pruebas de rendimiento para ver el impacto. Una prueba, focus-links.html, mostró una mejora enorme de alrededor del 1618%. Este descubrimiento se convirtió en la base para trabajos posteriores.
Cómo investigar la prueba lenta
Comencé a investigar por qué esa prueba específica era lenta con los cuadros delimitados. Todo lo que hizo la prueba fue enfocarse en varios vínculos, uno tras otro. Por lo tanto, el problema principal debe ser enfocarse en los elementos o el desplazamiento que se produjo con la acción de enfoque. Para probar esto, agregué {preventScroll: true}
a la llamada focus()
en la prueba de rendimiento y detuve el desplazamiento.
Con el desplazamiento inhabilitado, el tiempo de prueba disminuyó a 1.2 milisegundos cuando los cálculos del cuadro de límite estaban activos. Esto demostró que el desplazamiento era el verdadero problema.
Creé una prueba nueva llamada scroll-in-page.html para replicar la prueba focus-links, pero en lugar de usar el enfoque, se desplaza por los elementos con scrollIntoView()
. Probé el desplazamiento suave y el instantáneo, con y sin cálculos de cuadro de límite.
Los resultados mostraron que, con el desplazamiento instantáneo y los cuadros delimitadores, el proceso tardó alrededor de 66 ms. El desplazamiento fluido era aún más lento, alrededor de 124 ms. Cuando desactivamos los cuadros delimitados, no tardó nada porque no se activó ningún evento.
Conocíamos el caso, pero ¿por qué sucedía?
Ahora sabíamos que el desplazamiento es la fuente de mucha lentitud en la serialización de accesibilidad, pero aún teníamos que averiguar por qué. Para analizar esto, se usaron dos herramientas llamadas perf y pprof para desglosar el trabajo realizado en el proceso del navegador. Estas herramientas se suelen usar en C++ para la generación de perfiles. En los siguientes gráficos, se muestra un fragmento de la parte interesante.
Después de investigar, determinamos que el problema no era el código de deserialización en sí, sino la frecuencia de las llamadas a él. Para entender esto, debemos ver
cómo funcionan las actualizaciones de accesibilidad en Chromium. Las actualizaciones no se envían de forma individual. En su lugar, hay una ubicación central llamada AXObjectCache
que almacena todas las propiedades. Cuando cambia un nodo, varios métodos notifican a la caché para que marque ese nodo como no sincronizado para la serialización posterior. Luego, todas las propiedades de las notas no sincronizadas,
incluso las propiedades sin cambios, se serializan y se envían al navegador. Si bien este diseño simplifica el código y reduce la complejidad, ya que tiene una sola ruta de actualización, se vuelve lento cuando hay eventos rápidos de "marcar como no sincronizado", como los del desplazamiento. Lo único que cambia son los valores scrollX
y scrollY
. Sin embargo, serializamos el resto de las propiedades con ellos cada vez. La frecuencia de actualizaciones alcanzó más de veinte veces por segundo.
La serialización del cuadro delimitador aborda este problema mediante el uso de una ruta de serialización más rápida que solo envía los detalles del cuadro delimitador, lo que permite actualizaciones rápidas sin afectar a otras propiedades. Este método controla de manera eficiente los cambios en el cuadro de límite.
Corrección de desplazamiento
La solución era clara: incluir los desplazamientos de desplazamiento actuales con la serialización del cuadro de límite. Esto garantiza que las actualizaciones de desplazamiento se procesen a través de la ruta rápida, lo que mejora el rendimiento sin demoras innecesarias. Cuando empaquetamos los desplazamientos con datos de cuadro de límite, optimizamos el proceso para obtener actualizaciones más fluidas y eficientes, lo que crea una experiencia menos inestable para los usuarios que tienen la accesibilidad activada. La mejora después de la implementación de la corrección es de hasta el 825% en las pruebas de desplazamiento.
Simplificación del código
En este período, me enfoqué en la calidad del código como parte de un proyecto llamado Onion Soup, que simplifica el código reduciendo o quitando el código que se extiende innecesariamente entre las capas.
El objetivo del primer proyecto era optimizar la forma en que se serializan los datos de accesibilidad desde el renderizador hasta el navegador. Anteriormente, los datos debían pasar por una capa adicional antes de llegar a su destino, lo que agregaba complejidad innecesaria. Para simplificar este proceso, permitimos que los datos se envíen directamente, sin intermediarios.
Además, identificamos y quitamos algunos eventos desactualizados que causaban trabajo innecesario en el sistema, como uno que se activaba cuando se completaba un diseño. Los reemplazamos por una solución más eficiente.
También se realizaron otras pequeñas mejoras. Lamentablemente, no se registraron mejoras de rendimiento para estos, pero nos complace compartir que el código es mucho más claro y se autodocumenta mejor que antes. Esto ayuda mucho a allanar el camino para futuras mejoras de rendimiento. Puedes consultar los cambios reales en mi perfil de Gerrit.
Conclusión
Trabajar con el equipo de accesibilidad de Chromium ha sido un viaje gratificante. A través de la resolución de varios desafíos, desde la optimización del rendimiento del desplazamiento hasta la simplificación de la base de código, obtuve una comprensión más profunda del desarrollo en un proyecto de gran escala, además de aprender herramientas importantes para la generación de perfiles. Además, aprendí lo fundamental que es la accesibilidad para crear una Web inclusiva para todos. Las mejoras que realizamos no solo mejoran la experiencia del usuario que depende de las tecnologías de accesibilidad, sino que también contribuyen al rendimiento y la eficiencia generales del navegador.
Los resultados de rendimiento fueron impresionantes. Por ejemplo, el cambio a usar enumeraciones para programar tareas mejoró el rendimiento en más del 20%. Además, nuestra corrección del desplazamiento generó una reducción de hasta un 825% en las pruebas de desplazamiento. Los cambios de simplificación de código no solo lo hicieron más claro y más fácil de mantener, sino que también allanaron el camino para mejoras futuras.
Me gustaría expresar mi gratitud a Stefan Zager, Chris Harrelson y Mason Freed por su apoyo y orientación durante todo el año, y en especial a Aaron Leventhal, sin quien esta oportunidad no habría sido posible. También quiero agradecer a Tab Atkins-Bittner y al equipo de GSoC por su apoyo.
Para quienes buscan contribuir a un proyecto significativo y desarrollar sus habilidades, recomiendo que se involucren en Chromium. Es una excelente manera de aprender, y programas como Google Summer of Code ofrecen un excelente punto de partida para tu recorrido.