La compatibilidad con subprocesos de WebAssembly se lanzó en Chrome 70 con una prueba de origen.
WebAssembly (Wasm) permite compilar código escrito en C++ y otros lenguajes para que se ejecute en la Web. Una función muy útil de las aplicaciones nativas es la capacidad de usar subprocesos, una primitiva para el procesamiento paralelo. La mayoría de los desarrolladores de C y C++ están familiarizados con pthreads, que es una API estandarizada para la administración de subprocesos en una aplicación.
El grupo de la comunidad de WebAssembly estuvo trabajando para incorporar subprocesos a la Web y habilitar aplicaciones reales de varios subprocesos. Como parte de este esfuerzo, V8 implementó la compatibilidad necesaria para los subprocesos en el motor de WebAssembly, disponible a través de una prueba de origen. Las pruebas de origen permiten que los desarrolladores experimenten con nuevas funciones web antes de que se estandaricen por completo. Esto nos permite recopilar comentarios del mundo real de desarrolladores intrépidos, lo que es fundamental para validar y mejorar las funciones nuevas.
La versión de Chrome 70 admite subprocesos para WebAssembly, y alentamos a los desarrolladores interesados a que comiencen a usarlos y nos envíen sus comentarios.
¿Threads? ¿Qué sucede con los trabajadores?
Los navegadores admiten el paralelismo a través de Web Workers desde 2012 en Chrome 4. De hecho, es normal escuchar términos como "en el subproceso principal", etcétera. Sin embargo, los Web Workers no comparten datos mutables entre sí, sino que dependen del paso de mensajes para la comunicación. De hecho, Chrome asigna un nuevo motor V8 para cada uno de ellos (llamados aislamientos). Los aislamientos no comparten código compilado ni objetos de JavaScript y, por lo tanto, no pueden compartir datos mutables como pthreads.
Por otro lado, los subprocesos de WebAssembly son subprocesos que pueden compartir la misma memoria de Wasm. El almacenamiento subyacente de la memoria compartida se realiza con un SharedArrayBuffer, una primitiva de JavaScript que permite compartir el contenido de un solo ArrayBuffer simultáneamente entre los trabajadores. Cada subproceso de WebAssembly se ejecuta en un Web Worker, pero su memoria de Wasm compartida les permite funcionar de manera muy similar a como lo hacen en las plataformas nativas. Esto significa que las aplicaciones que usan subprocesos de Wasm son responsables de administrar el acceso a la memoria compartida, como en cualquier aplicación tradicional con subprocesos. Existen muchas bibliotecas de código escritas en C o C++ que usan pthreads, y se pueden compilar en Wasm y ejecutar en modo de subprocesos verdadero, lo que permite que más núcleos trabajen en los mismos datos de forma simultánea.
Un ejemplo sencillo
Este es un ejemplo de un programa simple en "C" que usa subprocesos.
#include <pthread.h>
#include <stdio.h>
// Calculate Fibonacci numbers shared function
int fibonacci(int iterations) {
int val = 1;
int last = 0;
if (iterations == 0) {
return 0;
}
for (int i = 1; i < iterations; i++) {
int seq;
seq = val + last;
last = val;
val = seq;
}
return val;
}
// Start function for the background thread
void *bg_func(void *arg) {
int *iter = (void *)arg;
*iter = fibonacci(*iter);
return arg;
}
// Foreground thread and main entry point
int main(int argc, char *argv[]) {
int fg_val = 54;
int bg_val = 42;
pthread_t bg_thread;
// Create the background thread
if (pthread_create(&bg_thread, NULL, bg_func, &bg_val)) {
perror("Thread create failed");
return 1;
}
// Calculate on the foreground thread
fg_val = fibonacci(fg_val);
// Wait for background thread to finish
if (pthread_join(bg_thread, NULL)) {
perror("Thread join failed");
return 2;
}
// Show the result from background and foreground threads
printf("Fib(42) is %d, Fib(6 * 9) is %d\n", bg_val, fg_val);
return 0;
}
Ese código comienza con la función main(), que declara 2 variables: fg_val y bg_val. También hay una función llamada fibonacci(), a la que llamarán ambos subprocesos en este ejemplo. La función main()
crea un subproceso en segundo plano con pthread_create(),
cuya tarea es calcular el valor de la secuencia de números de Fibonacci correspondiente al
valor de la variable bg_val. Mientras tanto, la función main() que se ejecuta en el subproceso en primer plano lo calcula para la variable fg_val. Una vez que el subproceso en segundo plano haya terminado de ejecutarse, se imprimirán los resultados.
Compila para admitir subprocesos
Primero, debes tener instalado el SDK de emscripten, preferiblemente la versión 1.38.11 o posterior. Para compilar nuestro código de ejemplo con subprocesos habilitados para ejecutarse en el navegador, debemos pasar un par de marcas adicionales al compilador emcc de emscripten. Nuestra línea de comandos se ve de la siguiente manera:
emcc -O2 -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=2 -o test.js test.c
El argumento de línea de comandos "-s USE_PTHREADS=1" activa la compatibilidad con subprocesos para
el módulo de WebAssembly compilado, y el argumento "-s PTHREAD_POOL_SIZE=2"
le indica al compilador que genere un grupo de dos (2) subprocesos.
Cuando se ejecute el programa, de forma interna, se cargará el módulo de WebAssembly,
se creará un Web Worker para cada uno de los subprocesos del grupo de subprocesos, se compartirá el módulo
con cada uno de los trabajadores, en este caso, son 2, y se usarán cada vez que se realice una llamada a
pthread_create(). Cada trabajador crea una instancia del
módulo de Wasm con la misma memoria, lo que les permite cooperar. Los cambios más recientes de V8 en la versión 7.0 comparten el código nativo compilado de los módulos de Wasm que se pasan entre los trabajadores, lo que permite que incluso las aplicaciones muy grandes se escalen a muchos trabajadores. Ten en cuenta que tiene sentido asegurarse de que el tamaño del grupo de subprocesos sea igual a la
cantidad máxima de subprocesos que necesita tu aplicación, o bien puede fallar la creación de subprocesos.
Al mismo tiempo, si el tamaño del grupo de subprocesos es demasiado grande, crearás
Web Workers innecesarios que no harán nada más que usar memoria.
Cómo probarla
La forma más rápida de probar nuestro módulo de WebAssembly es activar la compatibilidad experimental con los subprocesos de WebAssembly en Chrome 70 y versiones posteriores. Navega a la
URL about://flags en tu navegador, como se muestra a continuación:
A continuación, busca el parámetro de configuración experimental de subprocesos de WebAssembly, que se ve de la siguiente manera:
Cambia el parámetro de configuración a Enabled como se muestra a continuación y, luego, reinicia el navegador.
Después de reiniciar el navegador, podemos intentar cargar el módulo de WebAssembly con subprocesos con una página HTML mínima, que contenga solo este contenido:
<!DOCTYPE html>
<html>
<title>Threads test</title>
<body>
<script src="test.js"></script>
</body>
</html>
Para probar esta página, deberás ejecutar algún tipo de servidor web y cargarlo desde tu navegador. Esto hará que el módulo de WebAssembly se cargue y se ejecute. Si abres las Herramientas para desarrolladores, verás el resultado de la ejecución, que debería ser similar a la siguiente imagen:
Nuestro programa de WebAssembly con subprocesos se ejecutó correctamente. Te recomendamos que pruebes tu propia aplicación con subprocesos siguiendo los pasos descritos anteriormente.
Pruebas de campo con una prueba de origen
Probar los subprocesos activando las marcas experimentales en el navegador es una buena opción para el desarrollo, pero si quieres probar tu aplicación en el campo, puedes hacerlo con lo que se conoce como una prueba de origen.
Las pruebas de origen te permiten probar funciones experimentales con tus usuarios obteniendo un token de prueba vinculado a tu dominio. Luego, puedes implementar tu app y esperar que funcione en un navegador que admita la función que estás probando (en este caso, Chrome 70 y versiones posteriores). Para obtener tu propio token y ejecutar una prueba de origen, usa el formulario de solicitud que se encuentra aquí.
Alojamientos nuestro ejemplo simple anterior con un token de prueba de origen, para que puedas probarlo por tu cuenta sin necesidad de compilar nada.
Si quieres ver lo que 4 subprocesos que se ejecutan en paralelo pueden hacer por el arte ASCII, también debes mirar esta demostración.
Envíanos tus comentarios
Los subprocesos de WebAssembly son una primitiva nueva muy útil para portar aplicaciones a la Web. Ahora es posible ejecutar aplicaciones y bibliotecas de C y C++ que requieren compatibilidad con pthreads en el entorno de WebAssembly.
Buscamos comentarios de los desarrolladores que prueben esta función, ya que ayudarán a informar el proceso de estandarización y validar su utilidad. La mejor manera de enviar comentarios es informar problemas o participar en el proceso de estandarización en el Grupo de la comunidad de WebAssembly.