WebAssembly-Threads in Chrome 70 ausprobieren

Die Unterstützung von WebAssembly-Threads wurde in Chrome 70 im Rahmen eines Ursprungstests bereitgestellt.

Alex Danilo

WebAssembly (Wasm) ermöglicht die Kompilierung von in C++ und anderen Programmiersprachen geschriebenen Code, um ihn im Web auszuführen. Ein sehr nützliches Merkmal nativer Anwendungen ist die Möglichkeit, Threads zu verwenden, eine primitive für parallele Berechnung. Die meisten C- und C++-Entwickler sind mit pthreads vertraut, einer standardisierten API für die Threadverwaltung in einer Anwendung.

Die WebAssembly Community Group arbeitet daran, Threads im Web verfügbar zu machen, um echte Multithread-Anwendungen zu ermöglichen. Im Rahmen dieser Bemühungen hat V8 die erforderliche Unterstützung für Threads in der WebAssembly-Engine implementiert, die über einen Ursprungstest verfügbar ist. Mit Ursprungstests können Entwickler mit neuen Webfunktionen experimentieren, bevor sie vollständig standardisiert sind. So erhalten wir Feedback von unerschrockenen Entwicklern aus der Praxis, das für die Validierung und Verbesserung neuer Funktionen von entscheidender Bedeutung ist.

Die Chrome 70-Version unterstützt Threads für WebAssembly. Wir empfehlen interessierten Entwicklern, sie zu verwenden und uns Feedback zu geben.

Threads? Was ist mit den Workern?

Browser unterstützen die Parallelität über Web Worker in Chrome 4 seit 2012. Tatsächlich ist es normal, Begriffe wie "im Hauptthread" usw. zu hören. Web Worker geben jedoch keine änderbaren Daten zwischen ihnen weiter, sondern greifen für die Kommunikation auf die Übergabe von Nachrichten zurück. Tatsächlich weist Chrome jedem dieser Engines eine neue V8-Engine zu (Isolations genannt). Isolates teilen weder kompilierten Code noch JavaScript-Objekte und können daher keine änderbaren Daten wie pthreads teilen.

WebAssembly-Threads sind dagegen Threads, die denselben Wasm-Arbeitsspeicher teilen können. Der zugrunde liegende Speicher für den freigegebenen Speicher erfolgt mit SharedArrayBuffer, einer JavaScript-Primitive, mit der sich die Inhalte eines einzelnen ArrayBuffers gleichzeitig zwischen Workern freigeben lassen. Jeder WebAssembly-Thread wird in einem Web Worker ausgeführt, mit dem gemeinsam genutzten Wasm-Speicher können sie jedoch ähnlich wie auf nativen Plattformen arbeiten. Dies bedeutet, dass die Anwendungen, die Wasm-Threads verwenden, wie bei jeder herkömmlichen Thread-Anwendung für die Verwaltung des Zugriffs auf den freigegebenen Speicher zuständig sind. Es gibt viele in C oder C++ geschriebene Codebibliotheken, die pthreads verwenden. Diese können in Wasm kompiliert und im True-Threaded-Modus ausgeführt werden, sodass mehr Kerne gleichzeitig mit denselben Daten arbeiten können.

Ein einfaches Beispiel

Hier ist ein Beispiel für ein einfaches 'C'-Programm, das Threads verwendet.

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

Dieser Code beginnt mit der Funktion main(), die die beiden Variablen fg_val und bg_val deklariert. Es gibt auch die Funktion fibonacci(), die in diesem Beispiel von beiden Threads aufgerufen wird. Die Funktion main() erstellt mit pthread_create() einen Hintergrundthread, dessen Aufgabe darin besteht, den Fibonacci-Nummernsequenzwert zu berechnen, der dem Wert der Variablen bg_val entspricht. Währenddessen wird dies von der Funktion main(), die im Vordergrundthread ausgeführt wird, für die Variable fg_val berechnet. Sobald der Hintergrundthread ausgeführt wurde, werden die Ergebnisse ausgegeben.

Kompilieren Sie für Thread-Unterstützung

Zuerst muss das emscripten SDK installiert sein, vorzugsweise Version 1.38.11 oder höher. Damit unser Beispielcode mit aktivierten Threads zur Ausführung im Browser erstellt wird, müssen wir einige zusätzliche Flags an den Compiler emscripten emcc übergeben. Unsere Befehlszeile sieht so aus:

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

Das Befehlszeilenargument "-s USE_PTHREADS=1" aktiviert die Thread-Unterstützung für das kompilierte WebAssembly-Modul. Das Argument "-s PTHREAD_POOL_SIZE=2" weist den Compiler an, einen Pool von zwei (2) Threads zu generieren.

Bei der Ausführung des Programms wird das WebAssembly-Modul geladen, ein Web Worker für jeden Thread im Threadpool erstellt, das Modul mit jedem Worker geteilt (in diesem Fall sind es zwei) und diese werden bei jedem Aufruf von pthread_create() verwendet. Jeder Worker instanziiert das Wasm-Modul mit demselben Arbeitsspeicher, sodass sie zusammenarbeiten können. Die neuesten Änderungen in V8 in 7.0 verwenden den kompilierten nativen Code von Wasm-Modulen, die zwischen Workern übergeben werden, sodass selbst sehr große Anwendungen auf viele Worker skaliert werden können. Hinweis: Es ist sinnvoll, dafür zu sorgen, dass die Threadpoolgröße der maximalen Anzahl von Threads entspricht, die Ihre Anwendung benötigt. Andernfalls kann die Threaderstellung fehlschlagen. Wenn der Threadpool zu groß ist, entstehen gleichzeitig unnötige Web Worker, die nichts weiter tun als Arbeitsspeicher.

So probierst du es aus:

Am schnellsten können Sie unser WebAssembly-Modul testen, wenn Sie die Unterstützung für experimentelle WebAssembly-Threads ab Chrome 70 aktivieren. Rufe in deinem Browser die URL about://flags auf:

Seite mit Chrome-Flags

Suchen Sie als Nächstes nach der experimentellen WebAssembly-Thread-Einstellung, die so aussieht:

Einstellung für WebAssembly-Threads

Ändern Sie die Einstellung wie unten gezeigt zu Aktiviert und starten Sie dann den Browser neu.

Einstellung für WebAssembly-Threads aktiviert

Nachdem der Browser neu gestartet wurde, können wir versuchen, das WebAssembly-Modul mit Threads mit einer minimalen HTML-Seite zu laden, die nur folgenden Inhalt enthält:

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

Um diese Seite auszuprobieren, müssen Sie einen Webserver ausführen und über Ihren Browser laden. Dadurch wird das WebAssembly-Modul geladen und ausgeführt. Wenn Sie die Entwicklertools öffnen, sehen Sie die Ausgabe der Ausführung. In der Konsole sollten Sie in etwa so etwas wie das folgende Ausgabebild sehen:

Konsolenausgabe des Fibonacci-Programms

Unser WebAssembly-Programm mit Threads wurde erfolgreich ausgeführt! Wir empfehlen Ihnen, anhand der oben beschriebenen Schritte Ihre eigene Thread-Anwendung auszuprobieren.

Praxistest mit einem Ursprungstest

Für Entwicklungszwecke ist das Testen von Threads durch Aktivieren von experimentellen Flags im Browser in Ordnung. Wenn Sie Ihre Anwendung jedoch vor Ort testen möchten, können Sie dies mit einem sogenannten Ursprungstest tun.

Mit Ursprungstests können Sie experimentelle Funktionen mit Ihren Nutzern testen. Dazu fordern Sie ein Testtoken an, das mit Ihrer Domain verknüpft ist. Sie können Ihre Anwendung dann bereitstellen und erwarten, dass sie in einem Browser funktioniert, der die getestete Funktion unterstützt (in diesem Fall Chrome 70 und höher). Verwenden Sie dieses Antragsformular, um ein eigenes Token für die Ausführung eines Ursprungstests zu erhalten.

Wir haben unser einfaches Beispiel von oben mit einem Ursprungstesttoken gehostet, sodass Sie es selbst ausprobieren können, ohne etwas erstellen zu müssen.

Wenn Sie wissen möchten, wie sich vier parallel laufende Threads für die ASCII-Art eignen, sollten Sie sich auch diese Demo ansehen.

Feedback geben

WebAssembly-Threads sind eine äußerst nützliche neue Primitive für die Portierung von Anwendungen in das Web. Es ist jetzt möglich, C- und C++-Anwendungen und -Bibliotheken auszuführen, die pthreads-Unterstützung in der WebAssembly-Umgebung benötigen.

Wir bitten um Feedback von Entwicklern, die diese Funktion ausprobieren, da sie sowohl den Standardisierungsprozess als auch die Nützlichkeit der Funktion validieren. Am besten senden Sie Feedback, indem Sie Probleme melden und/oder am Standardisierungsprozess in der WebAssembly Community Group teilnehmen.