Cómo aceleramos una aplicación web 20 veces con WebAssembly

Cómo aceleramos una aplicación web 20 veces con WebAssembly

En este artículo se analiza un caso de mejora de una aplicación web mediante la sustitución de cálculos en JavaScript por WebAssembly.

¿Qué es WebAssembly?

En pocas palabras, es un formato binario de instrucciones para una máquina virtual de pila. A menudo se le llama lenguaje de programación (su acrónimo es Wasm), pero eso no es correcto. Este formato de instrucciones se ejecuta en el navegador junto a JavaScript.

Es importante mencionar que WebAssembly se puede obtener al compilar código fuente en lenguajes como C/C++, Rust y Go. Se emplea tipificación estática y un modelo de memoria denominado plano. Como se mencionó anteriormente, el código se almacena en un formato binario compacto, lo que permite su ejecución casi tan rápida como si la aplicación se iniciara mediante la línea de comandos. Estas capacidades han contribuido al aumento de popularidad de WebAssembly.

Recordamos: para todos los lectores de «Habr» — un descuento de 10,000 rublos al registrarse en cualquier curso de Skillbox usando el código promocional «Habr».

Skillbox recomienda: Curso práctico «Desarrollador Móvil PRO».

En la actualidad, Wasm se utiliza en diversas aplicaciones, desde juegos como Doom 3 hasta aplicaciones portadas a la web como Autocad y Figma. También se aplica en áreas como los cálculos sin servidor.

Este artículo presenta un ejemplo de uso de Wasm para mejorar un servicio web analítico. Para ilustrar, tomamos una aplicación existente, escrita en C, que se compilará en WebAssembly. El resultado se utilizará para reemplazar partes poco eficientes de JS.

Transformación de la aplicación

En el ejemplo se utilizará el servicio web fastq.bio, diseñado para genetistas. Esta herramienta permite evaluar la calidad de la secuenciación (decodificación) de ADN.

Aquí hay un ejemplo de la aplicación en funcionamiento:

Cómo aceleramos una aplicación web 20 veces con WebAssembly

No es necesario proporcionar detalles del proceso, ya que son bastante complejos para los no especialistas, pero en resumen, los científicos pueden comprender a través de la infografía mencionada si el proceso de secuenciación de ADN se desarrolló sin contratiempos y qué problemas surgieron.

Este servicio tiene alternativas, como programas de escritorio. Sin embargo, fastq.bio permite acelerar el trabajo al visualizar los datos. En la mayoría de los otros casos, es fundamental saber manejar la línea de comandos, pero no todos los genetistas tienen la experiencia necesaria.

Todo funciona de manera sencilla. La entrada son datos presentados en forma de archivo de texto. Este archivo es generado por herramientas especializadas para secuenciación. En el archivo se encuentra una lista de secuencias de ADN y la evaluación de calidad para cada nucleótido. El formato de archivo es .fastq, de ahí el nombre del servicio.

Implementación en JavaScript

El primer paso del usuario al trabajar con fastq.bio es seleccionar el archivo correspondiente. Usando el objeto File, la aplicación lee una muestra aleatoria de datos del archivo y procesa este lote. La tarea de JavaScript aquí es realizar operaciones de cadena simples y contar métricas. Una de ellas es la cantidad de nucleótidos A, C, G y T en diferentes fragmentos de ADN.

Después de calcular las métricas necesarias, se visualizan utilizando Plotly.js, y el servicio comienza a trabajar con una nueva muestra de datos. La división en fragmentos se realiza para mejorar la calidad de la experiencia del usuario. Si se trabaja con todos los datos a la vez, el proceso se congelará durante un tiempo, ya que los archivos de resultados de secuenciación ocupan cientos de gigabytes de espacio. El servicio toma secciones de datos de entre 0.5 y 1 MB y trabaja con ellas paso a paso, construyendo datos gráficos.

Así es como funciona:

Cómo aceleramos una aplicación web 20 veces con WebAssembly

En el rectángulo rojo se coloca el algoritmo de transformación de cadenas para obtener la visualización. Esta es la parte más intensiva en cálculos del servicio. Vale la pena intentar reemplazarla con Wasm.

Probando WebAssembly

Para evaluar la posibilidad de usar Wasm, el equipo del proyecto se dedicó a buscar soluciones listas para crear métricas QC (QC — control de calidad) basadas en archivos fastq. La búsqueda se centró en herramientas escritas en C, C++ o Rust, para poder portar el código a WebAssembly. Además, la herramienta no debía ser 'rudimentaria', se necesitaba un servicio que ya hubiera sido verificado por científicos.

Como resultado, se eligió seqtk. La aplicación es bastante popular, es de código abierto y el idioma fuente es C.

Antes de la transformación en Wasm, vale la pena revisar el principio de compilación de seqtk para escritorio. Según el Makefile, esto es lo que se necesita:

# Compile to binary
$ gcc seqtk.c 
   -o seqtk 
   -O2 
   -lm 
   -lz

En principio, se puede compilar seqtk utilizando Emscripten. Si no lo tienes, podemos hacerlo de la siguiente manera: Docker.

$ docker pull robertaboukhalil/emsdk:1.38.26
$ docker run -dt --name wasm-seqtk robertaboukhalil/emsdk:1.38.26

Si lo deseas, también puedes compilarlo tú mismo, pero eso lleva tiempo.

Dentro del contenedor se puede usar emcc como una alternativa a gcc sin problemas:

# Compile to WebAssembly
$ emcc seqtk.c 
    -o seqtk.js 
    -O2 
    -lm 
    -s USE_ZLIB=1 
    -s FORCE_FILESYSTEM=1

Mínimos cambios:

En lugar de la salida a un archivo binario, Emscripten utiliza .wasm y .js para generar archivos, que se aplican para ejecutar el módulo WebAssembly.

Se utiliza la bandera USE_ZLIB para soportar la biblioteca zlib. Esta biblioteca es común y está portada a WebAssembly, y Emscripten la incluye en el proyecto.

Se activa el sistema de archivos virtual de Emscripten. Esto es un sistema de archivos similar a POSIX, que opera en la memoria dentro del navegador. Cuando se actualiza la página, la memoria se borra.

Para entender por qué se necesita un sistema de archivos virtual, es útil comparar cómo se ejecuta seqtk desde la línea de comandos con cómo se ejecuta el módulo WebAssembly compilado.

# On the command line
$ ./seqtk fqchk data.fastq
 
# In the browser console
> Module.callMain(["fqchk", "data.fastq"])

Acceder al sistema de archivos virtual es necesario para no tener que reescribir seqtk para entrada de cadena en lugar de entrada de archivo. En este caso, la fragmento de datos se mapea como el archivo data.fastq en el sistema de archivos virtual, con la llamada a main() seqtk en él.

Aquí está la nueva arquitectura:

Cómo aceleramos una aplicación web 20 veces con WebAssembly

El diagrama muestra que en lugar de realizar cálculos en el hilo principal del navegador se utilizan WebWorkers. Este método permite realizar cálculos en segundo plano sin afectar la capacidad de respuesta del navegador. Además, el controlador de WebWorker inicia el Worker, gestionando su interacción con el hilo principal.

El comando seqtk se ejecuta a través de un Worker en el archivo montado. Después de que el Worker completa la ejecución, proporciona el resultado en forma de Promise. Cuando el mensaje es recibido por el hilo principal, el resultado se utiliza para actualizar los gráficos. Y así en varias iteraciones.

¿Qué hay sobre el rendimiento de WebAssembly?

Para evaluar el cambio en el rendimiento, el equipo del proyecto utilizó el parámetro de operaciones de lectura por segundo. El tiempo de construcción de gráficos interactivos no se tiene en cuenta, ya que en ambas implementaciones se utiliza JavaScript.

Con la solución 'listo para usar', el incremento en rendimiento fue de nueve veces.

Cómo aceleramos una aplicación web 20 veces con WebAssembly

Este es un excelente resultado, pero resulta que hay otra oportunidad para optimizarlo. La cuestión es que un gran número de resultados del análisis QC no son utilizados por seqtk, por lo que pueden eliminarse. Si se hace esto, el resultado en comparación con JS mejora en 13 veces.

Cómo aceleramos una aplicación web 20 veces con WebAssembly

Esto se logró comentando las instrucciones printf().

Pero eso no es todo. La cuestión es que en esta etapa, fastq.bio recibe los resultados del análisis mediante la invocación de distintas funciones C. Cualquiera de ellas calcula su propio conjunto de características, por lo que cada fragmento del archivo se leía dos veces.

Para abordar este problema, se decidió combinar dos funciones en una. Como resultado, el rendimiento se incrementó 20 veces.

Cómo aceleramos una aplicación web 20 veces con WebAssembly

Vale la pena señalar que tal resultado excepcional no se puede alcanzar siempre. En algunos casos, el rendimiento disminuye, por lo que es necesario evaluar cada caso específico.

En conclusión, se puede afirmar que Wasm realmente permite mejorar el rendimiento de la aplicación, pero debe usarse con prudencia.

Skillbox recomienda:

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster