WebAssembly-Funktionserkennung

Hier erfahren Sie, wie Sie die neuesten WebAssembly-Funktionen verwenden und gleichzeitig Nutzer in allen Browsern unterstützen.

WebAssembly 1.0 wurde vor vier Jahren veröffentlicht, aber die Entwicklung endete damit noch nicht. Neue Funktionen werden im Rahmen des Standardisierungsprozesses für Angebote hinzugefügt. Wie bei neuen Funktionen im Web können die Implementierungsreihenfolge und -fristen bei verschiedenen Suchmaschinen erheblich variieren. Wenn Sie diese neuen Funktionen verwenden möchten, müssen Sie sicherstellen, dass keiner Ihrer Nutzer ausgeschlossen wird. In diesem Artikel erfahren Sie, wie Sie dies erreichen können.

Einige neue Funktionen verbessern die Codegröße, indem neue Anweisungen für gängige Operationen hinzugefügt werden, einige bieten leistungsstarke Funktionen zur Leistungssteigerung und andere verbessern die Entwicklererfahrung und die Integration mit dem Rest des Webs.

Die vollständige Liste der Angebote und ihre jeweiligen Phasen finden Sie im offiziellen Repository. Den Implementierungsstatus in Suchmaschinen können Sie auf der offiziellen Seite zur Produkt-Roadmap verfolgen.

Um sicherzustellen, dass Nutzer aller Browser Ihre Anwendung verwenden können, müssen Sie sich überlegen, welche Funktionen Sie verwenden möchten. Teilen Sie sie dann je nach Browserunterstützung in Gruppen auf. Kompilieren Sie dann Ihre Codebasis separat für jede dieser Gruppen. Schließlich müssen im Browser unterstützte Funktionen ermittelt und das entsprechende JavaScript- und Wasm-Bundle geladen werden.

Funktionen auswählen und gruppieren

Gehen wir diese Schritte durch, indem wir einen beliebigen Funktionssatz als Beispiel auswählen. Nehmen wir an, ich habe festgestellt, dass ich aus Gründen der Größe und Leistung in meiner Bibliothek SIMD, Threads und die Ausnahmebehandlung verwenden möchte. Ihr Browser wird so unterstützt:

<ph type="x-smartling-placeholder">
</ph> Eine Tabelle, in der die Browserunterstützung für die ausgewählten Funktionen angezeigt wird. <ph type="x-smartling-placeholder">
</ph> Sehen Sie sich diese Feature-Tabelle unter webassembly.org/roadmap an.

Sie können Browser in die folgenden Kohorten unterteilen, um sicherzustellen, dass jeder Nutzer die bestmögliche Erfahrung hat:

  • Chrome-basierte Browser: Threads, SIMD und die Ausnahmebehandlung werden unterstützt.
  • Firefox: Thread und SIMD werden unterstützt, die Ausnahmebehandlung nicht.
  • Safari: Threads werden unterstützt, SIMD und Ausnahmebehandlung nicht.
  • Andere Browser: nur grundlegende WebAssembly-Unterstützung vorausgesetzt.

Diese Aufschlüsselung ist nach Funktionsunterstützung in der neuesten Version der einzelnen Browser unterteilt. Moderne Browser sind grundsätzlich gültig und werden automatisch aktualisiert, sodass Sie sich in den meisten Fällen nur um die neueste Version kümmern müssen. Solange Sie jedoch WebAssembly als Fallback-Kohorte aufnehmen, können Sie auch Nutzern mit veralteten Browsern eine funktionierende Anwendung zur Verfügung stellen.

Kompilierung für verschiedene Feature-Sets

WebAssembly verfügt nicht über eine integrierte Möglichkeit, unterstützte Funktionen in der Laufzeit zu erkennen. Daher müssen alle Anweisungen im Modul auf dem Ziel unterstützt werden. Aus diesem Grund müssen Sie den Quellcode für jeden dieser verschiedenen Funktionssätze separat in Wasm kompilieren.

Jede Toolchain und jedes Build-System ist anders. Informationen zur Feinabstimmung dieser Features finden Sie in der Dokumentation Ihres eigenen Compilers. Der Einfachheit halber verwende ich im folgenden Beispiel eine C++-Bibliothek mit einer einzelnen Datei und zeige, wie sie mit Emscripten kompiliert wird.

Ich verwende SIMD über die SSE2-Emulation, Threads über die Unterstützung der Pthreads-Bibliothek und wähle zwischen der Wasm-Ausnahmebehandlung und der Fallback-JavaScript-Implementierung:

# First bundle: threads + SIMD + Wasm exceptions
$ emcc main.cpp -o main.threads-simd-exceptions.mjs -pthread -msimd128 -msse2 -fwasm-exceptions
# Second bundle: threads + SIMD + JS exceptions fallback
$ emcc main.cpp -o main.threads-simd.mjs -pthread -msimd128 -msse2 -fexceptions
# Third bundle: threads + JS exception fallback
$ emcc main.cpp -o main.threads.mjs -pthread -fexceptions
# Fourth bundle: basic Wasm with JS exceptions fallback
$ emcc main.cpp -o main.basic.mjs -fexceptions

Der C++-Code selbst kann #ifdef __EMSCRIPTEN_PTHREADS__ und #ifdef __SSE2__ verwenden, um bei der Kompilierung bedingt zwischen parallelen (Threads und SIMD) Implementierungen derselben Funktionen und den seriellen Implementierungen zu wählen. Dies würde so aussehen:

void process_data(std::vector<int>& some_input) {
#ifdef __EMSCRIPTEN_PTHREADS__
#ifdef __SSE2__
  // …implementation using threads and SIMD for max speed
#else
  // …implementation using threads but not SIMD
#endif
#else
  // …fallback implementation for browsers without those features
#endif
}

Für die Ausnahmebehandlung sind keine #ifdef-Anweisungen erforderlich, da sie unabhängig von der zugrunde liegenden Implementierung, die über die Kompilierungs-Flags ausgewählt wurde, auf dieselbe Weise von C++ verwendet werden kann.

Das richtige Bundle wird geladen

Nachdem Sie die Sets für alle Funktionskohorten erstellt haben, müssen Sie das richtige aus der Haupt-JavaScript-Anwendung laden. Dazu müssen Sie zuerst ermitteln, welche Funktionen im aktuellen Browser unterstützt werden. Dazu können Sie die Bibliothek wasm-feature-detect verwenden. Wenn Sie es mit dem dynamischen Import kombinieren, können Sie das am besten optimierte Bundle in jedem Browser laden:

import { simd, threads, exceptions } from 'https://unpkg.com/wasm-feature-detect?module';

let initModule;
if (await threads()) {
  if (await simd()) {
    if (await exceptions()) {
      initModule = import('./main.threads-simd-exceptions.mjs');
    } else {
      initModule = import('./main.threads-simd.mjs');
    }
  } else {
    initModule = import('./main.threads.mjs');
  }
} else {
  initModule = import('./main.basic.mjs');
}

const Module = await initModule();
// now you can use `Module` Emscripten object like you normally would

Abschließende Wörter

In diesem Beitrag habe ich Ihnen gezeigt, wie Sie Sets für verschiedene Funktionsgruppen auswählen, erstellen und zwischen ihnen wechseln können.

Wenn die Anzahl der Funktionen steigt,kann die Anzahl der Featurekohorten möglicherweise nicht mehr verwaltet werden. Um dieses Problem zu beheben, können Sie Feature-Kohorten basierend auf Ihren realen Nutzerdaten auswählen, weniger beliebte Browser überspringen und diesen auf etwas weniger optimale Kohorten zurückgreifen lassen. Solange Ihre Anwendung noch für alle Benutzer funktioniert, kann dieser Ansatz ein angemessenes Gleichgewicht zwischen progressiver Verbesserung und Laufzeitleistung bieten.

Zukünftig wird WebAssembly möglicherweise eine integrierte Möglichkeit erhalten, unterstützte Funktionen zu erkennen und zwischen verschiedenen Implementierungen derselben Funktion innerhalb des Moduls zu wechseln. Ein solcher Mechanismus wäre jedoch selbst eine Post-MVP-Funktion, die Sie anhand des obigen Ansatzes erkennen und laden müssen. Bis dahin bleibt dieser Ansatz die einzige Möglichkeit, Code mit den neuen WebAssembly-Funktionen in allen Browsern zu erstellen und zu laden.