Thread WebAssembly pronti per la prova in Chrome 70

Il supporto dei thread WebAssembly è stato implementato in Chrome 70 nell'ambito di una prova di origine.

Alex Danilo

WebAssembly (Wasm) consente di compilare il codice scritto in C++ e in altri linguaggi per l'esecuzione sul web. Una funzionalità molto utile delle applicazioni native è la possibilità di utilizzare i thread, un elemento di base per i calcoli paralleli. La maggior parte degli sviluppatori C e C++ conosce pthreads, un'API standardizzata per la gestione dei thread in un'applicazione.

Il gruppo della community WebAssembly si sta adoperando per portare i thread sul web al fine di consentire applicazioni multithread reali. Nell'ambito di questo impegno, V8 ha implementato il supporto necessario per i thread nel motore WebAssembly, disponibile tramite una prova dell'origine. Le prove di origine consentono agli sviluppatori di sperimentare nuove funzionalità web prima che vengano completamente standardizzate. Questo ci consente di raccogliere feedback reali da sviluppatori intrepidi, il che è fondamentale per convalidare e migliorare le nuove funzionalità.

La release di Chrome 70 supporta i thread per WebAssembly e invitiamo gli sviluppatori interessati a iniziare a utilizzarli e a inviarci un feedback.

Thread? Che ne dici di Workers?

I browser supportano il parallelismo tramite Web Worker dal 2012 in Chrome 4. Infatti, è normale sentire termini come "nel thread principale" e così via. Tuttavia, i Web Worker non condividono dati mutabili tra loro, ma si basano sulla trasmissione di messaggi per la comunicazione. In effetti, Chrome alloca un nuovo motore V8 per ciascuno di questi (chiamati istanze). Gli istanze isolate non condividono né il codice compilato né gli oggetti JavaScript e, pertanto, non possono condividere dati mutabili come i pthread.

I thread WebAssembly, invece, sono thread che possono condividere la stessa memoria Wasm. L'archiviazione sottostante della memoria condivisa viene eseguita con un SharedArrayBuffer, una primitiva JavaScript che consente di condividere i contenuti di un singolo Arraybu contemporaneamente tra i worker. Ogni thread WebAssembly viene eseguito in un worker web, ma la memoria Wasm condivisa consente loro di funzionare in modo molto simile alle piattaforme native. Ciò significa che le applicazioni che utilizzano i thread Wasm sono responsabili della gestione dell'accesso alla memoria condivisa come in qualsiasi applicazione con threading tradizionale. Esistono molte librerie di codice scritte in C o C++ che utilizzano pthreads e che possono essere compilate in Wasm ed eseguite in modalità con threading effettivo, consentendo a più core di lavorare contemporaneamente sugli stessi dati.

Un esempio semplice

Ecco un esempio di un semplice programma in C che utilizza i thread.

#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;
}

Questo codice inizia con la funzione main() che dichiara due variabili fg_val e bg_val. Esiste anche una funzione chiamata fibonacci(), che verrà chiamata da entrambi i thread in questo esempio. La funzione main() crea un thread in background utilizzando pthread_create() il cui compito è calcolare il valore della sequenza di numeri di Fibonacci corrispondente al valore della variabile bg_val. Nel frattempo, la funzione main() in esecuzione nel thread in primo piano lo calcola per la variabile fg_val. Una volta completata l'esecuzione del thread in background, i risultati vengono stampati.

Compila per il supporto dei thread

Innanzitutto, devi avere installato l'SDK emscripten, preferibilmente la versione 1.38.11 o successiva. Per creare il nostro codice di esempio con thread abilitati per l'esecuzione nel browser, dobbiamo passare un paio di flag aggiuntivi al compilatore emscripten emcc. La nostra riga di comando è la seguente:

emcc -O2 -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=2 -o test.js test.c

L'argomento della riga di comando "-s USE_PTHREADS=1" attiva il supporto dei thread per il modulo WebAssembly compilato e l'argomento "-s PTHREAD_POOL_SIZE=2" indica al compilatore di generare un pool di due (2) thread.

Quando il programma viene eseguito, sotto il cofano carica il modulo WebAssembly, crea un web worker per ogni thread nel pool di thread, condivide il modulo con ciascuno dei worker, in questo caso sono 2, che verranno utilizzati ogni volta che viene eseguita una chiamata a pthread_create(). Ogni worker crea un'istanza del modulo Wasm con la stessa memoria, consentendogli di cooperare. Le ultime modifiche apportate dalla V8 alla versione 7.0 condividono il codice nativo compilato dei moduli Wasm che vengono passati tra i worker, il che consente la scalabilità anche di applicazioni molto grandi per molti worker. Tieni presente che è opportuno assicurarsi che le dimensioni del pool di thread siano uguali al numero massimo di thread di cui ha bisogno l'applicazione, altrimenti la creazione dei thread potrebbe non riuscire. Allo stesso tempo, se le dimensioni del pool di thread sono troppo grandi, creerai worker web non necessari che rimarranno inutilizzati occupando memoria.

Come provarlo

Il modo più rapido per testare il nostro modulo WebAssembly è attivare il supporto sperimentale dei thread WebAssembly in Chrome 70 e versioni successive. Vai all'URL about://flags nel browser come mostrato di seguito:

Pagina delle segnalazioni di Chrome

Poi, individua l'impostazione dei thread WebAssembly sperimentali, che ha il seguente aspetto:

Impostazione dei thread WebAssembly

Imposta l'opzione su Abilitato come mostrato di seguito, quindi riavvia il browser.

Impostazione dei thread WebAssembly attivata

Dopo il riavvio del browser, possiamo provare a caricare il modulo WebAssembly con conversazioni in thread con una pagina HTML minima, contenente solo i seguenti contenuti:

<!DOCTYPE html>
<html>
  <title>Threads test</title>
  <body>
    <script src="test.js"></script>
  </body>
</html>

Per provare questa pagina, dovrai eseguire un server web e caricarla dal tuo browser. Il modulo WebAssembly verrà caricato ed eseguito. Se apri DevTools viene visualizzato l'output dell'esecuzione, dovresti vedere qualcosa di simile all'immagine di output qui sotto nella console:

Output della console del programma di Fibonacci

Il nostro programma WebAssembly con thread è stato eseguito correttamente. Ti invitiamo a provare la tua applicazione con thread seguendo i passaggi descritti sopra.

Test sul campo con una prova dell'origine

Provare i thread attivando i flag sperimentali nel browser va bene a fini di sviluppo, ma se vuoi testare l'applicazione sul campo, puoi farlo con quella che è nota come prova dell'origine.

Le prove dell'origine ti consentono di provare le funzionalità sperimentali con i tuoi utenti ottenendo un token di test associato al tuo dominio. Puoi quindi eseguire il deployment della tua app e aspettarti che funzioni in un browser in grado di supportare la funzionalità che stai testando (in questo caso, Chrome 70 e versioni successive). Per ottenere il tuo token per eseguire una prova dell'origine, utilizza il modulo di richiesta.

Abbiamo ospitato il nostro semplice esempio sopra utilizzando un token di prova dell'origine, quindi puoi provarlo tu stesso senza dover creare nulla.

Se vuoi vedere cosa possono fare quattro thread in esecuzione in parallelo per la grafica ASCII, devi guardare anche questa demo.

Inviaci i tuoi commenti

I thread WebAssembly sono una nuova primitiva estremamente utile per la portabilità delle applicazioni sul web. Ora è possibile eseguire applicazioni e librerie C e C++ che richiedono il supporto di pthreads nell'ambiente WebAssembly.

Stiamo cercando il feedback degli sviluppatori che provano questa funzionalità, in quanto ci aiuterà a informare il processo di standardizzazione e a convalidarne l'utilità. Il modo migliore per inviare feedback è segnalare i problemi e/o partecipare al processo di standardizzazione nel gruppo della community WebAssembly.