Thread WebAssembly pronti per la prova in Chrome 70

Il supporto dei thread WebAssembly è stato spedito in Chrome 70 durante una prova dell'origine.

Alex Danilo

WebAssembly (Wasm) consente l'esecuzione sul web della compilazione di codice scritto in C++ e in altri linguaggi. Una caratteristica molto utile delle applicazioni native è la capacità di utilizzare i thread, una funzionalità primitiva per il calcolo parallelo. La maggior parte degli sviluppatori di C e C++ avrà familiarità con pthreads, un'API standardizzata per la gestione dei thread in un'applicazione.

Il WebAssembly Community Group si sta adoperando per portare i thread sul web per abilitare applicazioni multi-thread 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 dell'origine consentono agli sviluppatori di sperimentare nuove funzionalità web prima che vengano completamente standardizzate. Questo ci consente di raccogliere feedback reali da sviluppatori intrepidi, che è fondamentale per convalidare e migliorare le nuove funzionalità.

La release Chrome 70 supporta i thread per WebAssembly; invitiamo gli sviluppatori interessati a iniziare a utilizzarli e a fornirci il loro feedback.

Thread? E i lavoratori?

I browser supportano il parallelismo tramite i web worker dal 2012 in Chrome 4; in effetti è normale sentire termini come "sul thread principale" e così via. Tuttavia, i web worker non condividono dati modificabili tra loro, ma si basano sulla trasmissione dei messaggi per la comunicazione. Chrome alloca un nuovo motore V8 per ognuno di essi (chiamati isolati). Gli isolati non condividono né codice compilato né oggetti JavaScript, pertanto non possono condividere dati modificabili come i pthread.

I thread di 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 la condivisione contemporanea di un singolo contenuto di ArrayBuffer tra worker. Ogni thread WebAssembly viene eseguito in un web worker, ma la loro memoria Wasm condivisa gli consente di lavorare 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 conversazioni in thread tradizionale. Esistono molte librerie di codice scritte in C o C++ che utilizzano pthreads, che possono essere compilate in Wasm ed eseguite in modalità con thread reale, in modo che più core possano lavorare contemporaneamente sugli stessi dati.

Un esempio semplice

Ecco un esempio di programma "C" semplice 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 denominata fibonacci(), che verrà chiamata da entrambi i thread in questo esempio. La funzione main() crea un thread in background utilizzando pthread_create() la cui attività è calcolare il valore della sequenza del numero di Fibonacci corrispondente al valore della variabile bg_val. Nel frattempo, la funzione main() in esecuzione nel thread in primo piano la 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, dovresti aver installato l'SDK emscripten, preferibilmente la versione 1.38.11 o successive. 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 del threading per il modulo WebAssembly compilato, mentre l'argomento "-s PTHREAD_POOL_SIZE=2" indica al compilatore di generare un pool di due (2) thread.

Quando il programma viene eseguito, in background caricherà il modulo WebAssembly, creerà un Web Worker per ciascuno dei thread nel pool di thread, condividerà il modulo con ciascuno dei worker, in questo caso 2, che verranno utilizzati ogni volta che viene effettuata una chiamata a pthread_create(). Ogni worker crea un'istanza del modulo Wasm con la stessa memoria, consentendo la cooperazione. Le ultime modifiche apportate dalla V8 alla versione 7.0 condividono il codice nativo compilato dei moduli Wasm che vengono passati tra i worker, consentendo la scalabilità anche per applicazioni di grandi dimensioni. Nota, ha senso assicurarsi che la dimensione del pool di thread corrisponda al numero massimo di thread richiesti dall'applicazione, altrimenti la creazione dei thread potrebbe non riuscire. Allo stesso tempo, se le dimensioni del pool di thread sono troppo grandi, creerai web worker non necessari, che si rilassano, e non fanno altro che usare la memoria.

Come fare una prova

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 dei flag di Chrome

Quindi, trova l'impostazione sperimentale dei thread WebAssembly, simile alla seguente:

Impostazione dei thread WebAssembly

Modifica l'impostazione in Attivata come mostrato di seguito, quindi riavvia il browser.

Impostazione dei thread WebAssembly abilitata

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 caricarlo dal tuo browser. Di conseguenza, il modulo WebAssembly verrà caricato ed eseguito. L'apertura di DevTools mostra l'output dell'esecuzione e dovresti vedere qualcosa di simile all'immagine di output riportata di seguito nella console:

Output della console dal programma Fibonacci

Il nostro programma WebAssembly con thread è stato eseguito correttamente. Ti invitiamo a provare la tua applicazione con conversazioni in thread seguendo la procedura descritta sopra.

Test sul campo con una prova dell'origine

Provare i thread attivando i flag sperimentali nel browser va bene per scopi di sviluppo, ma se vuoi testare la tua applicazione sul campo, puoi farlo con una 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 dell'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 applicazione qui.

Abbiamo ospitato il nostro semplice esempio riportato sopra utilizzando un token di prova dell'origine, in modo che tu possa provarlo autonomamente senza dover creare nulla.

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

Inviaci i tuoi commenti

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

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