WebAssembly- und WebGPU-Verbesserungen für schnellere Web-KI, Teil 1

Hier erfahren Sie, wie WebAssembly- und WebGPU-Verbesserungen die Leistung des maschinellen Lernens im Web verbessern.

Austin Eng
Austin Eng
Deepti Gandluri
Deepti Gandluri
François Beaufort
François Beaufort

KI-Inferenz im Web

Wir alle kennen: KI verändert unsere Welt. Das Web ist da keine Ausnahme.

Dieses Jahr wurde in Chrome auf generativer KI basierende Funktionen hinzugefügt, z. B. die Erstellung benutzerdefinierter Designs und/oder Hilfe beim Verfassen eines ersten Textentwurfs. Aber KI ist viel mehr als das. KI kann selbst Anwendungen bereichern.

Auf Webseiten können intelligente Komponenten für das Sehen eingebettet sein, z. B. für die Erkennung von Gesichtern oder Gesten, für die Audioklassifizierung oder für die Spracherkennung. Im letzten Jahr hat Generative AI einen enormen Aufschwung erlebt, einschließlich einiger wirklich beeindruckender Demos von Large Language Models im Web. Weitere Informationen finden Sie unter Practical On-Device AI for Web Developers (Praktische On-Device-KI für Webentwickler).

KI-Inferenzen sind im Web heute auf vielen Geräten verfügbar. Die KI-Verarbeitung kann auf der Webseite selbst erfolgen, wobei die Hardware auf dem Gerät des Nutzers genutzt wird.

Dies ist aus mehreren Gründen leistungsfähig:

  • Geringere Kosten: Durch das Ausführen von Inferenzen auf dem Browserclient können die Serverkosten erheblich reduziert werden. Dies kann besonders bei Abfragen mit generativer KI nützlich sein, die im Vergleich zu regulären Abfragen um einiges kostspieliger sein können.
  • Latenz: Bei Anwendungen, die besonders empfindlich auf Latenz reagieren, z. B. Audio- oder Videoanwendungen, verringert sich die Latenz, wenn die gesamte Verarbeitung auf dem Gerät stattfindet.
  • Datenschutz: Die clientseitige Ausführung bietet außerdem das Potenzial, eine neue Klasse von Anwendungen zu erschließen, die erhöhten Datenschutz erfordern, bei denen keine Daten an den Server gesendet werden können.

So werden KI-Arbeitslasten aktuell im Web ausgeführt

Heute erstellen Anwendungsentwickler und Forscher Modelle mithilfe von Frameworks, sie werden im Browser mit einer Laufzeit wie Tensorflow.js oder ONNX Runtime Web ausgeführt und Laufzeiten nutzen zur Ausführung Web-APIs.

Alle diese Laufzeiten werden schließlich auf der CPU über JavaScript oder WebAssembly oder auf der GPU über WebGL oder WebGPU ausgeführt.

Diagramm zur heutigen Ausführung von KI-Arbeitslasten im Web

ML-Arbeitslasten

Durch Arbeitslasten für maschinelles Lernen (ML) werden Tensoren durch einen Graphen von Rechenknoten geleitet. Tensor sind die Ein- und Ausgaben dieser Knoten, die eine große Menge an Datenberechnungen durchführen.

Das ist aus folgenden Gründen wichtig:

  • Tensoren sind sehr große Datenstrukturen, die Berechnungen auf Modellen mit Milliarden von Gewichten durchführen
  • Skalierung und Inferenz können zu Datenparallelität führen. Dies bedeutet, dass für alle Elemente in den Tensoren dieselben Operationen ausgeführt werden.
  • ML erfordert keine Präzision. Möglicherweise benötigen Sie eine 64-Bit-Gleitkommazahl, um auf dem Mond zu landen, aber möglicherweise nur ein Meer mit 8-Bit-Zahlen oder weniger für die Gesichtserkennung.

Erfreulicherweise haben Chipdesigner zusätzliche Funktionen hinzugefügt, damit die Modelle schneller und kühler laufen und sogar überhaupt ausgeführt werden können.

In der Zwischenzeit arbeiten wir hier in den WebAssembly- und WebGPU-Teams daran, diese neuen Funktionen den Webentwicklern zur Verfügung zu stellen. Wenn Sie Entwickler von Webanwendungen sind, werden Sie diese Low-Level-Primitive wahrscheinlich nicht häufig verwenden. Wir gehen davon aus, dass die von Ihnen verwendeten Toolchains oder Frameworks neue Funktionen und Erweiterungen unterstützen, sodass Sie mit minimalen Änderungen an Ihrer Infrastruktur davon profitieren können. Wenn Sie jedoch die Leistung Ihrer Anwendungen manuell optimieren möchten, sind diese Funktionen für Ihre Arbeit relevant.

WebAssembly

WebAssembly (Wasm) ist ein kompaktes, effizientes Bytecode-Format, das Laufzeiten verstehen und ausführen können. Er wurde entwickelt, um die zugrunde liegenden Hardwarefunktionen zu nutzen, sodass er mit beinahe nativer Geschwindigkeit ausgeführt werden kann. Der Code wird validiert und in einer speichersicheren Sandbox-Umgebung ausgeführt.

Informationen zu Wasm-Modulen werden mit einer dichten Binärcodierung dargestellt. Im Vergleich zu einem textbasierten Format bedeutet dies eine schnellere Decodierung, ein schnelleres Laden und eine geringere Speichernutzung. Sie ist portabel in dem Sinne, dass sie keine Annahmen über die zugrunde liegende Architektur trifft, die in modernen Architekturen noch nicht üblich sind.

Die WebAssembly-Spezifikation ist iterativ und wird in einer offenen W3C-Community-Gruppe erarbeitet.

Das Binärformat setzt keine Annahmen über die Hostumgebung voraus und ist daher auch für Einbettungen außerhalb des Webs geeignet.

Ihre Anwendung kann einmal kompiliert und dann überall ausgeführt werden: auf einem Desktop, Laptop, Telefon oder einem anderen Gerät mit Browser. Weitere Informationen finden Sie unter WriteOnce, Ausführung überall mit WebAssembly.

Illustration eines Laptops, Tablets und Smartphones

Die meisten Produktionsanwendungen, die KI-Inferenz im Web ausführen, nutzen WebAssembly sowohl für die CPU-Berechnung als auch für die Schnittstelle zu Spezial-Computing. In nativen Anwendungen können Sie sowohl auf universelle als auch auf spezielle Rechenleistung zugreifen, da die Anwendung auf Gerätefunktionen zugreifen kann.

Aus Gründen der Portabilität und Sicherheit prüfen wir im Internet sorgfältig, welche Primitive verfügbar sind. Dadurch wird ein Gleichgewicht zwischen der Zugänglichkeit des Internets und der maximalen Leistung der Hardware geschaffen.

WebAssembly ist eine portable Abstraktion von CPUs, sodass alle Wasm-Inferenzen auf der CPU ausgeführt werden. Dies ist zwar nicht die leistungsstärkste Wahl, aber CPUs sind allgemein verfügbar und können auf den meisten Geräten für die meisten Arbeitslasten verwendet werden.

Bei kleineren Arbeitslasten, z. B. Text- oder Audioarbeitslasten, wäre die GPU teuer. Es gibt eine Reihe von aktuellen Beispielen, in denen Wasm die richtige Wahl ist:

In Open-Source-Demos können Sie noch mehr entdecken, z. B. whisper-tiny, llama.cpp und Gemma2B running in the browser.

Anwendungen ganzheitlich gestalten

Sie sollten Primitive basierend auf dem jeweiligen ML-Modell, der Anwendungsinfrastruktur und der insgesamt für die Nutzer vorgesehenen Anwendungserfahrung auswählen

Beispielsweise sind bei der Erkennung von Gesichts-Sehenswürdigkeiten von MediaPipe CPU-Inferenz und GPU-Inferenz vergleichbar (bei Ausführung auf einem Apple M1-Gerät). Es gibt jedoch Modelle, bei denen die Varianz deutlich höher sein kann.

Bei ML-Arbeitslasten berücksichtigen wir eine vollständige Anwendungsansicht und berücksichtigen dabei die Framework-Autoren und Anwendungspartner, um die am häufigsten angeforderten Verbesserungen zu entwickeln und bereitzustellen. Diese lassen sich grob in drei Kategorien unterteilen:

  • Leistungskritische CPU-Erweiterungen verfügbar machen
  • Ausführung größerer Modelle aktivieren
  • Nahtlose Interoperabilität mit anderen Web-APIs ermöglichen

Schnelleres Computing

Derzeit enthält die WebAssembly-Spezifikation nur einen Teil der Anweisungen, die wir im Web veröffentlichen. Allerdings kommen immer wieder neuere Anweisungen hinzu, die die Lücke zwischen nativer und WebAssembly-Leistung vergrößern.

Denken Sie daran, dass ML-Modelle nicht immer ein hohes Maß an Präzision erfordern. Locked SIMD ist ein Vorschlag, der einige der strengen, nicht deterministischen Anforderungen reduziert, was zu einer schnelleren Codegenerierung für einige Vektoroperationen führt, die ein hohes Leistungsrisiko darstellen. Darüber hinaus führt lockere SIMD neue Punktprodukte und FMA-Anweisungen ein, die vorhandene Arbeitslasten um das 1,5- bis 3-Fache beschleunigen. Sie wurde in Chrome 114 veröffentlicht.

Beim Gleitkommaformat mit halber Genauigkeit werden 16 Bit für IEEE FP16 anstelle der 32 Bits für Werte mit einfacher Genauigkeit verwendet. Im Vergleich zu Werten mit einfacher Genauigkeit bietet die Verwendung von Werten mit halber Genauigkeit, geringere Speicheranforderungen, die das Trainieren und Bereitstellen größerer neuronaler Netzwerke sowie eine geringere Arbeitsspeicherbandbreite, mehrere Vorteile. Geringere Genauigkeit beschleunigt die Datenübertragung und mathematische Operationen.

Größere Modelle

Verweise in den linearen Wasm-Speicher werden als ganze 32-Bit-Zahlen dargestellt. Dies hat zwei Auswirkungen: Die Heap-Größen sind auf 4 GB beschränkt (wenn Computer deutlich mehr physischen RAM haben) und der Anwendungscode, der auf Wasm abzielt, muss mit einer 32-Bit-Pointer-Größe kompatibel sein.

Besonders bei großen Modellen, wie wir sie heute haben, kann das Laden dieser Modelle in WebAssembly eingeschränkt sein. Beim Vorschlag Memory64 werden diese Einschränkungen entfernt, da der lineare Speicher größer als 4 GB ist und dem Adressbereich nativer Plattformen entspricht.

Wir haben eine voll funktionsfähige Implementierung in Chrome und werden voraussichtlich im Laufe des Jahres verfügbar sein. Bis dahin können Sie Tests mit der Kennzeichnung chrome://flags/#enable-experimental-webassembly-features durchführen und uns Feedback geben.

Bessere Web-Interoperabilität

WebAssembly könnte der Einstiegspunkt für spezielle Computing-Zwecke im Web sein.

Mit WebAssembly lassen sich GPU-Anwendungen ins Web bringen. Dies bedeutet, dass dieselbe C++-App, die auf dem Gerät ausgeführt werden kann, mit geringfügigen Änderungen auch im Web ausgeführt werden kann.

Die Wasm-Compiler-Toolchain Emscripten hat bereits Bindungen für WebGPU. Da es der Einstiegspunkt für KI-Inferenz im Web ist, ist es wichtig, dass Wasm nahtlos mit dem Rest der Webplattform interagieren kann. In diesem Bereich arbeiten wir an verschiedenen Vorschlägen.

Integration von JavaScript Promise (JSPI)

Typische C- und C++-Anwendungen sowie viele andere Programmiersprachen werden üblicherweise anhand einer synchronen API geschrieben. Dies bedeutet, dass die Anwendung die Ausführung stoppt, bis der Vorgang abgeschlossen ist. Solche blockierenden Anwendungen sind in der Regel intuitiver zu schreiben als Anwendungen, die asynchron arbeiten.

Wenn teure Vorgänge den Hauptthread blockieren, können sie die E/A-Vorgänge blockieren und die Verzögerung ist für die Nutzer sichtbar. Es besteht eine Diskrepanz zwischen einem synchronen Programmiermodell nativer Anwendungen und dem asynchronen Modell des Webs. Dies ist besonders bei Legacy-Anwendungen problematisch, deren Portierung teuer wäre. Emscripten bietet eine Möglichkeit dazu mit Asyncify, aber dies ist nicht immer die beste Option, da der Code größer und nicht so effizient ist.

Im folgenden Beispiel wird Fibonacci mithilfe von JavaScript-Promis für Addition berechnet.

long promiseFib(long x) {
 if (x == 0)
   return 0;
 if (x == 1)
   return 1;
 return promiseAdd(promiseFib(x - 1), promiseFib(x - 2));
}
// promise an addition
EM_ASYNC_JS(long, promiseAdd, (long x, long y), {
  return Promise.resolve(x+y);
});
emcc -O3 fib.c -o b.html -s ASYNCIFY=2

Achten Sie in diesem Beispiel auf Folgendes:

  • Das EM_ASYNC_JS-Makro generiert den gesamten erforderlichen Glue Code, sodass wir mithilfe von JSPI auf das Ergebnis des Promise-Objekts zugreifen können, so wie es auch bei einer normalen Funktion der Fall wäre.
  • Die spezielle Befehlszeilenoption -s ASYNCIFY=2. Dadurch wird die Option zum Generieren von Code aufgerufen, der mithilfe von JSPI eine Schnittstelle zu JavaScript-Importen herstellt, die Promise zurückgeben.

Weitere Informationen über JSPI, ihre Verwendung und ihre Vorteile finden Sie unter Einführung der WebAssembly JavaScript Promise Integration API auf v8.dev. Weitere Informationen zum aktuellen Ursprungstest

Speichersteuerung

Entwickler haben nur sehr wenig Kontrolle über Wasm-Speicher. hat das Modul einen eigenen Speicher. Alle APIs, die auf diesen Arbeitsspeicher zugreifen müssen, müssen ein- oder herauskopiert werden, und diese Nutzung kann sich summieren. Es kann beispielsweise sein, dass eine Grafikanwendung für jeden Frame ein- und herauskopieren muss.

Mit dem Vorschlag Memory Control soll eine präzisere Kontrolle über den linearen Wasm-Speicher ermöglicht und die Anzahl der Kopien in der Anwendungspipeline reduziert werden. Dieser Vorschlag befindet sich in Phase 1. Wir erstellen einen Prototyp in V8, der JavaScript-Engine von Chrome, um die Entwicklung des Standards voranzutreiben.

Welches Back-End ist das richtige für Sie?

Die CPU ist zwar allgegenwärtig, aber nicht immer die beste Option. Spezielle Rechenleistungen auf der GPU oder auf Beschleunigern können eine um Größenordnung höhere Leistung bieten, insbesondere bei größeren Modellen und auf High-End-Geräten. Dies gilt sowohl für native als auch für Webanwendungen.

Welches Back-End Sie auswählen, hängt von der Anwendung, dem Framework oder der Toolchain sowie von anderen Faktoren ab, die sich auf die Leistung auswirken. Wir investieren jedoch weiterhin in Vorschläge, mit denen Core Wasm gut mit dem Rest der Webplattform und insbesondere mit WebGPU funktioniert.

Teil 2 weiterlesen