WebAssembly redujo el tiempo de carga de Figma en 3 veces

Un estudio de caso de WebAssembly

Ilustración creada para Figma por Chris Hamamoto

WebAssembly se lanzó recientemente en marzo pasado, pero ya ha generado mucha emoción en la comunidad web. Es un nuevo formato binario para el código de máquina que fue diseñado específicamente con los navegadores en mente. Debido a que las aplicaciones compiladas en WebAssembly pueden ejecutarse tan rápido como las aplicaciones nativas, tiene el potencial de cambiar la forma en que el software se escribe en la web.

Muchas personas han comenzado a experimentar con proyectos de WebAssembly de juguete, pero es difícil saber cuáles serán las mejoras de rendimiento en el mundo real a menos que tenga una gran base de código compatible para comparar. Debido a que nuestro producto está escrito en C ++, que puede compilarse fácilmente en WebAssembly, Figma es una demostración perfecta de la potencia de este nuevo formato. Si no ha usado Figma antes, es una herramienta de diseño de interfaz basada en navegador con un potente motor de representación WebGL 2D que admite documentos muy grandes.

Pensamos que nuestros hallazgos serían útiles para la comunidad web como un ejemplo del impacto de WebAssembly. Spoiler: es mucho más rápido.

Descripción general de WebAssembly

WebAssembly permite a los desarrolladores crear experiencias de calidad de escritorio en la web sin comprometer el rendimiento. Todos los principales navegadores están agregando soporte y dedicando recursos significativos para que sea lo más rápido posible.

Antes de WebAssembly, el código C ++ podía ejecutarse en el navegador compilándolo en un subconjunto de JavaScript conocido como asm.js. El subconjunto asm.js es básicamente JavaScript donde solo puede usar números (sin cadenas, objetos, etc.). Esto es todo lo que necesita para ejecutar código C ++, ya que todo en C ++ es un número o un puntero a un número, y los punteros también son números. El espacio de direcciones de C ++ es solo una matriz gigante de números de JavaScript y los punteros son solo índices en esa matriz.

WebAssembly es un reemplazo directo para asm.js que es más eficiente en todos los sentidos. Pero todavía asigna 1: 1 con asm.js, por lo que tiene las mismas limitaciones. Como solo puede cargar y almacenar números, necesita llamar al código JavaScript para hacer algo interesante (crear nodos DOM, hacer conexiones de red, etc.). El código de WebAssembly todavía está dentro del entorno limitado del navegador y solo puede usar las API del navegador a las que JavaScript tiene acceso.

Existen muchos beneficios al ejecutar código C ++ usando WebAssembly en lugar de compilarlo en JavaScript:

  • El formato es muy compacto, por lo que lleva menos tiempo transferirlo a través de la red que el JavaScript de compilación cruzada equivalente, incluso cuando está comprimido.
  • El formato fue diseñado para ser lo más rápido posible para que el navegador lo analice. Esto no es cierto para la sintaxis de JavaScript, que fue diseñada para humanos y contiene mucha redundancia y reglas adicionales que deben verificarse antes de que pueda ejecutarse. WebAssembly analiza alrededor de 20 veces más rápido que asm.js.
  • El código C ++ está muy optimizado por la cadena de herramientas LLVM incluso antes de que esté codificado en WebAssembly. Esto significa que el navegador puede traducirlo directamente al código nativo sin hacer ninguna optimización. En contraste, los navegadores no se saltan las optimizaciones para el código JavaScript porque generalmente está escrito a mano y necesita muchas capas de optimizaciones para ser rápido.
  • Es trivial para los navegadores almacenar en caché la traducción de un módulo WebAssembly a código nativo. Esto significa que la segunda vez que carga una página con un módulo WebAssembly, ¡prácticamente no hay tiempo de carga! Esto no es cierto para asm.js, que se mezcla con JavaScript normal y requiere un pase de verificación complejo para validar que realmente sigue las restricciones de asm.js.
  • WebAssembly tiene soporte nativo para enteros de 64 bits. JavaScript solo tiene números de coma flotante de 64 bits, por lo que solo admite enteros de 53 bits. Los enteros de 64 bits deben emularse en JavaScript, que puede ser mucho más lento.

Si está interesado en aprender más sobre WebAssembly, puedo recomendarle la presentación de introducción de alto nivel de Lin Clark y una publicación de bajo nivel de Rasmus Andersson, un diseñador e ingeniero que trabaja en Figma, que describe el formato de WebAssembly con excelente detalle. Los documentos oficiales tienen una guía de inicio si quieres jugar con ella tú mismo.

WebAssembly y Figma

El mayor beneficio que vimos al usar WebAssembly en Figma fue un tiempo de carga más rápido. Esto no es sorprendente ya que uno de los objetivos principales de WebAssembly es reducir el tiempo de carga. Cuando medimos el tiempo de carga en Figma, incluimos el tiempo para inicializar nuestra aplicación, descargar el archivo de diseño y renderizar todo el diseño por primera vez.

Como puede ver, nuestro tiempo de carga mejoró más de 3 veces después de cambiar a WebAssembly independientemente del tamaño del documento. Esta es una gran mejora para nuestros usuarios, quienes a menudo crean documentos de diseño muy grandes y frecuentemente cambian entre ellos.

Un beneficio adicional del tiempo de carga de WebAssembly que no se captura en este gráfico es que el tiempo de carga ya no depende del tamaño de la aplicación. Mientras los usuarios hayan cargado la aplicación anteriormente, el navegador ya debería tener la traducción de WebAssembly a código nativo en caché desde la última vez.

Esperábamos que la habilitación de WebAssembly también mejorara automáticamente el tamaño de descarga, pero no se redujo demasiado, especialmente después de la compresión. Resulta que comprimir el código asm.js solo termina un poco más grande que comprimir el código equivalente de WebAssembly.

WebAssembly estado del mundo

Al momento de escribir, el soporte de WebAssembly solo está habilitado de manera predeterminada en Firefox y Chrome. Edge y Safari todavía están trabajando en sus implementaciones y aún no las han lanzado. Esto probablemente cambiará pronto, por lo que debe verificar la entrada "¿Puedo usar" para obtener el estado más reciente?

Aunque el soporte de WebAssembly está habilitado tanto en Firefox como en Chrome, Figma en realidad solo usa WebAssembly en Firefox. Hemos detectado algunos errores importantes en la implementación de WebAssembly de Chrome que actualmente nos impiden habilitar WebAssembly en Chrome:

  • Error 719172: a diferencia de Firefox, Chrome no almacena en caché su código traducido de WebAssembly. Esto significa que toda la aplicación debe volverse a traducir cada vez que se carga la página, lo que en realidad hace que nuestra página se cargue más lentamente con WebAssembly que con asm.js.
  • Error 729768: la implementación de WebAssembly de Chrome se bloquea a veces al ejecutar el código de Figma. Suponemos que este es un error en V8 porque el mismo código funciona bien en Firefox.

Otra cosa a tener en cuenta es que todavía está surgiendo la compatibilidad con el depurador del navegador. No tenía prioridad para el MVP de WebAssembly y todavía no se implementó realmente. Mientras tanto, seguimos compilando asm.js en lugar de WebAssembly para la depuración.

El futuro de WebAssembly

WebAssembly es completamente nuevo y, por lo tanto, es comprensible que siga siendo difícil. Sin embargo, es un enfoque práctico y basado en principios para lograr el rendimiento nativo en la web que está bien preparado para convertirse en el núcleo del funcionamiento de la web.

Un aspecto emocionante de WebAssembly es que tiene ambiciones mucho más grandes que solo ser un objetivo para el código C ++. El diseño es apropiadamente de uso general y debería ser fácil agregar WebAssembly como un formato de salida alternativo para cualquier compilador que apunte a código nativo. Se está desarrollando un backend oficial de LLVM que, cuando esté terminado, facilitará que cualquier idioma que use la cadena de herramientas LLVM emita directamente el código de WebAssembly.

WebAssembly también tiene planes para exponer enlaces al recolector de basura de JavaScript en algún momento. Eso facilitará mucho la compilación cruzada de lenguajes recolectados de basura (Java, C #, etc.) a WebAssembly. Puede leer más sobre las próximas características de WebAssembly aquí.

Otro aspecto atractivo de WebAssembly es que es una parte integrada de la plataforma web. Es natural y se espera crear aplicaciones web que utilicen tanto módulos JavaScript como WebAssembly. Por ejemplo, si desea compatibilidad con JPEG 2000 universal, ya no tiene que esperar a que sea compatible con todos los navegadores. ¡Solo compila un decodificador en WebAssembly y llámalo desde JavaScript! Debería ser tan rápido como si fuera parte del navegador.

Esto podría mejorar el tiempo de iteración de la plataforma web. Los navegadores pueden centrarse en exponer primitivas de hardware de bajo nivel (WebGL, Bluetooth, USB, etc.) y dejar el desarrollo de bibliotecas de alto nivel a la comunidad, que puede evolucionar a un ritmo más rápido.

Aunque WebAssembly aún no está listo, Figma todavía está invirtiendo en él porque creemos que es una parte importante del futuro de la web.

¿Te gusta trabajar en aplicaciones web de última generación? Ven a trabajar a Figma!