Hay subprocesos de WebAssembly listos para probar en Chrome 70

La compatibilidad con subprocesos de WebAssembly se envió en Chrome 70 bajo una prueba de origen.

Alex Danilo

WebAssembly (Wasm) permite la compilación de 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 en paralelo. La mayoría de los desarrolladores de C y C++ están familiarizados con pthreads, una API estandarizada para la administración de subprocesos en una aplicación.

El WebAssembly Community Group ha estado trabajando para llevar subprocesos a la Web y habilitar aplicaciones multiproceso reales. 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 a los desarrolladores experimentar con nuevas funciones web antes de que se estandaricen por completo. Esto nos permite recopilar comentarios reales de desarrolladores intrépidos, lo cual es fundamental para validar y mejorar las funciones nuevas.

La versión 70 de Chrome admite subprocesos para WebAssembly y recomendamos a los desarrolladores interesados que comiencen a usarlos y nos envíen sus comentarios.

¿Hilos? ¿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. Sin embargo, los Web Workers no comparten datos mutables entre sí, sino que dependen del envío de mensajes para la comunicación. De hecho, Chrome asigna un nuevo motor V8 para cada uno de ellos (llamados aislamientos). Los objetos aislados no comparten código compilado ni objetos de JavaScript y, por lo tanto, no pueden compartir datos mutables como pthreads.

Los subprocesos de WebAssembly, por otro lado, son subprocesos que pueden compartir la misma memoria de Wasm. El almacenamiento subyacente de la memoria compartida se logra con un SharedArrayBuffer, una primitiva de JavaScript que permite compartir el contenido de un solo ArrayBuffer de forma simultánea entre los trabajadores. Cada subproceso de WebAssembly se ejecuta en un trabajador web, pero su memoria compartida de Wasm les permite funcionar de manera similar a como lo hacen en 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 con subprocesos tradicional. Existen muchas bibliotecas de código existentes escritas en C o C++ que usan pthreads, y que 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 "C" simple 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 que corresponde 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 se complete la ejecución del subproceso en segundo plano, se imprimirán los resultados.

Compila para admitir subprocesos

Primero, debes tener instalado el SDK de emscripten, preferentemente la versión 1.38.11 o una 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 emscripten emcc. 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 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, cargará el módulo WebAssembly, creará un trabajador web para cada uno de los subprocesos del grupo de subprocesos, 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 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 Wasm que se pasan entre los trabajadores, lo que permite que incluso 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 al número máximo de subprocesos que necesita tu aplicación, o podría fallar la creación de subprocesos. Al mismo tiempo, si el tamaño del grupo de subprocesos es demasiado grande, crearás trabajadores web innecesarios que no harán nada más que usar memoria.

Cómo probarlo

La forma más rápida de probar nuestro módulo de WebAssembly es activar la compatibilidad experimental con subprocesos de WebAssembly a partir de Chrome 70. Navega a la URL about://flags en tu navegador como se muestra a continuación:

Página de funciones experimentales de Chrome

A continuación, busca el parámetro de configuración experimental de subprocesos de WebAssembly, que se ve de la siguiente manera:

Configuración de subprocesos de WebAssembly

Cambia el parámetro de configuración a Habilitada como se muestra a continuación y, luego, reinicia el navegador.

Configuración de subprocesos de WebAssembly habilitada

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 se cargue y ejecute el módulo de WebAssembly. Si abres DevTools, se mostrará el resultado de la ejecución y deberías ver algo como la siguiente imagen de salida en la consola:

Resultado de la consola del programa de Fibonacci

Nuestro programa de WebAssembly con subprocesos se ejecutó correctamente. Te recomendamos que pruebes tu propia aplicación de subprocesos con los pasos descritos anteriormente.

Pruebas en el campo con una prueba de origen

Probar subprocesos activando marcas experimentales en el navegador está bien para fines de desarrollo, pero si deseas 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 mediante la obtención de 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 en adelante). Para obtener tu propio token y ejecutar una prueba de origen, usa el formulario de solicitud que se encuentra aquí.

Alojamos 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, entonces 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.

Queremos conocer los comentarios de los desarrolladores que prueben esta función, ya que nos ayudarán a informar el proceso de estandarización y a validar su utilidad. La mejor manera de enviar comentarios es informar problemas o participar en el proceso de estandarización en el grupo comunitario de WebAssembly.