Die Unterstützung von WebAssembly-Threads wurde in Chrome 70 im Rahmen eines Ursprungstests eingeführt.
WebAssembly (Wasm) ermöglicht die Kompilierung von Code, der in C++ und anderen Sprachen geschrieben wurde, um im Web ausgeführt zu werden. Eine sehr nützliche Funktion von nativen Anwendungen ist die Möglichkeit, Threads zu verwenden – ein Element für parallele Berechnungen. Die meisten C- und C++-Entwickler kennen pthreads, eine standardisierte API für die Threadverwaltung in einer Anwendung.
Die WebAssembly Community Group arbeitet daran, Threads ins Web zu bringen, um echte Multi-Thread-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 Origin Trial verfügbar ist. Mit Origin Trials können Entwickler neue Webfunktionen testen, bevor sie vollständig standardisiert sind. So erhalten wir wertvolles Feedback von mutigen Entwicklern, das für die Validierung und Verbesserung neuer Funktionen unerlässlich ist.
Die Chrome 70-Version unterstützt Threads für WebAssembly. Wir empfehlen interessierten Entwicklern, diese zu verwenden und uns Feedback zu geben.
Threads? Was ist mit Arbeitern?
Browser unterstützen seit 2012 in Chrome 4 Parallelität über Webworker. Begriffe wie „im Hauptthread“ sind daher keine Seltenheit. Webworker teilen jedoch keine veränderbaren Daten miteinander, sondern nutzen für die Kommunikation die Nachrichtenübertragung. Tatsächlich weist Chrome jedem von ihnen eine neue V8-Engine zu (sogenannte Isolate). Isolate teilen weder kompilierten Code noch JavaScript-Objekte und können daher keine veränderbaren Daten wie pthreads teilen.
WebAssembly-Threads hingegen können denselben Wasm-Speicher gemeinsam nutzen. Der zugrunde liegende Speicher des gemeinsamen Arbeitsspeichers wird mit einem SharedArrayBuffer realisiert, einer JavaScript-Primitivstruktur, die es ermöglicht, den Inhalt eines einzelnen ArrayBuffers gleichzeitig zwischen Workern zu teilen. Jeder WebAssembly-Thread wird in einem Webworker ausgeführt, aber dank des gemeinsamen Wasm-Speichers können sie ähnlich wie auf nativen Plattformen arbeiten. Das bedeutet, dass die Anwendungen, die Wasm-Threads verwenden, wie bei jeder herkömmlichen Anwendung mit Threads für die Verwaltung des Zugriffs auf den gemeinsamen Speicher verantwortlich sind. Es gibt viele Codebibliotheken, die in C oder C++ geschrieben wurden und pthreads verwenden. Diese können in Wasm kompiliert und im echten Thread-Modus ausgeführt werden, sodass mehr Kerne gleichzeitig an denselben Daten arbeiten können.
Ein einfaches Beispiel
Hier 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 zwei Variablen deklariert: fg_val
und bg_val
. Außerdem gibt es eine Funktion namens fibonacci()
, die von beiden Threads in diesem Beispiel aufgerufen wird. Die Funktion main()
erstellt mit pthread_create()
einen Hintergrund-Thread, der den Wert der Fibonacci-Zahlenfolge berechnet, der dem Wert der Variablen bg_val
entspricht. In der Zwischenzeit berechnet die Funktion main()
, die im Vordergrund-Thread ausgeführt wird, den Wert für die Variable fg_val
. Sobald der Hintergrund-Thread beendet ist, werden die Ergebnisse ausgegeben.
Für Thread-Unterstützung kompilieren
Sie müssen zuerst das emscripten SDK installiert haben, vorzugsweise Version 1.38.11 oder höher. Damit unser Beispielcode mit aktivierten Threads für die Ausführung im Browser erstellt werden kann, müssen wir dem emscripten-emcc-Compiler einige zusätzliche Flags übergeben. Unsere Befehlszeile sieht so aus:
emcc -O2 -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=2 -o test.js test.c
Mit dem Befehlszeilenargument „-s USE_PTHREADS=1
“ wird die Threadunterstützung für das kompilierte WebAssembly-Modul aktiviert. Das Argument „-s PTHREAD_POOL_SIZE=2
“ weist den Compiler an, einen Pool mit zwei (2) Threads zu generieren.
Wenn das Programm ausgeführt wird, wird das WebAssembly-Modul geladen, für jeden Thread im Threadpool ein Web Worker erstellt und das Modul für jeden Worker freigegeben. In diesem Fall sind es zwei, die verwendet werden, wenn pthread_create()
aufgerufen wird. Jeder Worker instanziiert das Wasm-Modul mit demselben Arbeitsspeicher, sodass eine Zusammenarbeit möglich ist. Die neuesten Änderungen an V8 in Version 7.0 teilen den kompilierten nativen Code von Wasm-Modulen, die zwischen Workern übergeben werden. So können auch sehr große Anwendungen auf viele Worker skaliert werden. Die Größe des Thread-Pools sollte der maximalen Anzahl der Threads entsprechen, die Ihre Anwendung benötigt. Andernfalls kann die Threaderstellung fehlschlagen.
Wenn die Größe des Threadpools jedoch zu groß ist, werden unnötige Webworker erstellt, die nichts tun und nur Arbeitsspeicher verbrauchen.
So kannst du es ausprobieren
Am schnellsten können Sie unser WebAssembly-Modul testen, indem Sie in Chrome 70 und höher die experimentelle Unterstützung für WebAssembly-Threads aktivieren. Rufen Sie die URL about://flags
in Ihrem Browser auf (siehe unten):
Suchen Sie als Nächstes die Einstellung für experimentelle WebAssembly-Threads. Sie sieht so aus:
Ändern Sie die Einstellung wie unten gezeigt in Aktiviert und starten Sie den Browser neu.
Nachdem der Browser neu gestartet wurde, können wir versuchen, das gethreadete WebAssembly-Modul mit einer minimalen HTML-Seite zu laden, die nur diesen Inhalt enthält:
<!DOCTYPE html>
<html>
<title>Threads test</title>
<body>
<script src="test.js"></script>
</body>
</html>
Wenn Sie diese Seite ausprobieren möchten, müssen Sie eine Art Webserver ausführen und die Seite über Ihren Browser laden. Dadurch wird das WebAssembly-Modul geladen und ausgeführt. Wenn Sie die DevTools öffnen, sehen Sie die Ausgabe des Laufs. In der Konsole sollte ungefähr das folgende Bild zu sehen sein:
Unser WebAssembly-Programm mit Threads wurde erfolgreich ausgeführt. Wir empfehlen Ihnen, die oben beschriebenen Schritte auszuführen, um Ihre eigene Anwendung mit Threads auszuprobieren.
Tests im Feld mit einem Ursprungstest
Das Testen von Threads durch Aktivieren experimenteller Flags im Browser ist für Entwicklungszwecke in Ordnung. Wenn Sie Ihre Anwendung jedoch in der Praxis testen möchten, können Sie dies mit einem sogenannten Ursprungstest tun.
Mit Origin Trials können Sie experimentelle Funktionen mit Ihren Nutzern testen. Dazu müssen Sie ein Testtoken abrufen, das mit Ihrer Domain verknüpft ist. Sie können Ihre App dann bereitstellen und davon ausgehen, dass sie in einem Browser funktioniert, der die getestete Funktion unterstützt (in diesem Fall ab Chrome 70). Wenn Sie ein eigenes Token für einen Ursprungstest benötigen, verwenden Sie dieses Antragsformular.
Wir haben unser einfaches Beispiel oben mit einem Test-Token für den Ursprung gehostet, damit Sie es selbst ausprobieren können, ohne etwas erstellen zu müssen.
Wenn Sie sehen möchten, was vier parallel ausgeführte Threads für ASCII-Art tun können, sollten Sie sich auch diese Demo ansehen.
Feedback geben
WebAssembly-Threads sind ein äußerst nützliches neues Primitive für die Portierung von Anwendungen ins Web. Es ist jetzt möglich, C- und C++-Anwendungen und ‑Bibliotheken auszuführen, die pthreads-Unterstützung in der WebAssembly-Umgebung erfordern.
Wir freuen uns über Feedback von Entwicklern, die diese Funktion ausprobieren. So können wir den Standardisierungsprozess besser planen und die Nützlichkeit der Funktion überprüfen. Am besten senden Sie Feedback, indem Sie Probleme melden und/oder sich an der Standardisierung in der WebAssembly Community Group beteiligen.