Es ist durchgehend schnell.
In meinen früheren Artikeln habe ich beschrieben, wie Sie mit WebAssembly das Bibliotheks-Ökosystem von C/C++ ins Web bringen können. Eine App, die C/C++-Bibliotheken intensiv nutzt, ist squoosh, unsere Web-App, mit der Sie Bilder mit einer Vielzahl von Codecs komprimieren können, die aus C++ in WebAssembly kompiliert wurden.
WebAssembly ist eine Low-Level-VM, die den Bytecode ausführt, der in .wasm
-Dateien gespeichert ist. Dieser Bytecode ist stark typisiert und so strukturiert, dass er viel schneller als JavaScript für das Hostsystem kompiliert und optimiert werden kann. WebAssembly bietet eine Umgebung zum Ausführen von Code, bei der von Anfang an Sandboxing und Einbettung berücksichtigt wurden.
Meiner Erfahrung nach werden die meisten Leistungsprobleme im Web durch erzwungenes Layout und übermäßiges Rendern verursacht. Gelegentlich muss eine App jedoch eine rechenintensive Aufgabe ausführen, die viel Zeit in Anspruch nimmt. WebAssembly kann hier helfen.
Der Hot Path
In Squoosh haben wir eine JavaScript-Funktion geschrieben, die einen Bildpuffer um Vielfache von 90 Grad dreht. OffscreenCanvas wäre dafür ideal, wird aber nicht von allen Browsern unterstützt, die wir im Blick hatten, und ist in Chrome etwas fehlerhaft.
Diese Funktion durchläuft jeden Pixel eines Eingabebilds und kopiert ihn an eine andere Position im Ausgabebild, um eine Drehung zu erreichen. Für ein Bild mit 4.094 × 4.096 Pixeln (16 Megapixel) wären über 16 Millionen Iterationen des inneren Codeblocks erforderlich, was wir als „Hot Path“ bezeichnen. Trotz dieser relativ großen Anzahl von Iterationen benötigen zwei von drei getesteten Browsern für die Aufgabe nur 2 Sekunden oder weniger. Eine akzeptable Dauer für diese Art von Interaktion.
for (let d2 = d2Start; d2 >= 0 && d2 < d2Limit; d2 += d2Advance) {
for (let d1 = d1Start; d1 >= 0 && d1 < d1Limit; d1 += d1Advance) {
const in_idx = ((d1 * d1Multiplier) + (d2 * d2Multiplier));
outBuffer[i] = inBuffer[in_idx];
i += 1;
}
}
Bei einem Browser dauert es jedoch über 8 Sekunden. Die Art und Weise, wie Browser JavaScript optimieren, ist sehr kompliziert und verschiedene Engines optimieren für unterschiedliche Dinge. Einige sind für die reine Ausführung optimiert, andere für die Interaktion mit dem DOM. In diesem Fall haben wir in einem Browser einen nicht optimierten Pfad erreicht.
WebAssembly hingegen ist vollständig auf die reine Ausführungsgeschwindigkeit ausgerichtet. Wenn wir also eine schnelle, vorhersehbare Leistung in allen Browsern für Code wie diesen wünschen, kann WebAssembly helfen.
WebAssembly für vorhersagbare Leistung
Im Allgemeinen können JavaScript und WebAssembly dieselbe Spitzenleistung erzielen. Bei JavaScript kann diese Leistung jedoch nur über den „schnellen Pfad“ erreicht werden. Es ist oft schwierig, diesen „schnellen Pfad“ beizubehalten. Ein wichtiger Vorteil von WebAssembly ist die vorhersehbare Leistung, auch browserübergreifend. Die strenge Typisierung und die Low-Level-Architektur ermöglichen es dem Compiler, stärkere Garantien zu geben, sodass WebAssembly-Code nur einmal optimiert werden muss und immer den „schnellen Pfad“ verwendet.
Für WebAssembly schreiben
Bisher haben wir C/C++-Bibliotheken in WebAssembly kompiliert, um ihre Funktionen im Web zu nutzen. Wir haben den Code der Bibliotheken nicht wirklich geändert, sondern nur wenig C/C++-Code geschrieben, um die Brücke zwischen dem Browser und der Bibliothek zu bilden. Dieses Mal ist unsere Motivation anders: Wir möchten etwas von Grund auf mit WebAssembly im Hinterkopf schreiben, damit wir die Vorteile von WebAssembly nutzen können.
WebAssembly-Architektur
Wenn Sie für WebAssembly schreiben, ist es hilfreich, etwas mehr darüber zu wissen, was WebAssembly eigentlich ist.
WebAssembly.org beschreibt WebAssembly so:
Wenn Sie C- oder Rust-Code in WebAssembly kompilieren, erhalten Sie eine .wasm
-Datei, die eine Moduldeklaration enthält. Diese Deklaration besteht aus einer Liste von „Importen“, die das Modul von seiner Umgebung erwartet, einer Liste von Exporten, die dieses Modul dem Host zur Verfügung stellt (Funktionen, Konstanten, Speicherblöcke) und natürlich den tatsächlichen binären Anweisungen für die darin enthaltenen Funktionen.
Etwas, das mir erst bewusst wurde, als ich mir das genauer ansah: Der Stack, der WebAssembly zu einer „stackbasierten virtuellen Maschine“ macht, wird nicht im Speicherbereich gespeichert, der von WebAssembly-Modulen verwendet wird. Der Stack ist vollständig VM-intern und für Webentwickler nicht zugänglich (außer über die Entwicklertools). Es ist daher möglich, WebAssembly-Module zu schreiben, die überhaupt keinen zusätzlichen Arbeitsspeicher benötigen und nur den VM-internen Stack verwenden.
In unserem Fall benötigen wir zusätzlichen Speicher, um einen beliebigen Zugriff auf die Pixel unseres Bildes zu ermöglichen und eine gedrehte Version dieses Bildes zu generieren. Dafür gibt es WebAssembly.Memory
.
Speicherverwaltung
Wenn Sie zusätzlichen Arbeitsspeicher verwenden, müssen Sie ihn in der Regel auch verwalten. Welche Teile des Arbeitsspeichers werden verwendet? Welche sind kostenlos?
In C gibt es beispielsweise die Funktion malloc(n)
, mit der ein Speicherbereich von n
aufeinanderfolgenden Bytes gefunden wird. Funktionen dieser Art werden auch als „Zuweisungsfunktionen“ bezeichnet.
Natürlich muss die Implementierung des verwendeten Allocators in Ihrem WebAssembly-Modul enthalten sein, was die Dateigröße erhöht. Die Größe und Leistung dieser Funktionen für die Speicherverwaltung können je nach verwendetem Algorithmus sehr unterschiedlich sein. Aus diesem Grund bieten viele Sprachen mehrere Implementierungen zur Auswahl an („dmalloc“, „emmalloc“, „wee_alloc“ usw.).
In unserem Fall kennen wir die Abmessungen des Eingabebilds (und damit die Abmessungen des Ausgabebilds), bevor wir das WebAssembly-Modul ausführen. Hier sahen wir eine Möglichkeit: Bisher haben wir den RGBA-Puffer des Eingabebildes als Parameter an eine WebAssembly-Funktion übergeben und das gedrehte Bild als Rückgabewert zurückgegeben. Um diesen Rückgabewert zu generieren, müssten wir den Zuweiser verwenden. Da wir jedoch wissen, wie viel Arbeitsspeicher insgesamt benötigt wird (zweimal die Größe des Eingabebilds, einmal für die Eingabe und einmal für die Ausgabe), können wir das Eingabebild mit JavaScript in den WebAssembly-Arbeitsspeicher einfügen, das WebAssembly-Modul ausführen, um ein zweites, gedrehtes Bild zu generieren, und dann das Ergebnis mit JavaScript zurücklesen. Wir können ganz ohne Speichermanagement auskommen.
Große Auswahl
Wenn Sie sich die ursprüngliche JavaScript-Funktion ansehen, die wir in WebAssembly umwandeln möchten, sehen Sie, dass es sich um reinen Berechnungscode ohne JavaScript-spezifische APIs handelt. Daher sollte es relativ einfach sein, diesen Code in eine beliebige Sprache zu portieren. Wir haben drei verschiedene Sprachen evaluiert, die zu WebAssembly kompiliert werden: C/C++, Rust und AssemblyScript. Die einzige Frage, die wir für jede der Sprachen beantworten müssen, lautet: Wie greifen wir auf den Rohspeicher zu, ohne Speichermanagementfunktionen zu verwenden?
C und Emscripten
Emscripten ist ein C-Compiler für das WebAssembly-Ziel. Emscripten soll als direkter Ersatz für bekannte C-Compiler wie GCC oder clang dienen und ist größtenteils flag-kompatibel. Dies ist ein wichtiger Teil der Emscripten-Mission, da das Kompilieren von vorhandenem C- und C++-Code in WebAssembly so einfach wie möglich sein soll.
Der Zugriff auf den Rohspeicher ist ein wesentlicher Bestandteil von C, und Zeiger sind genau aus diesem Grund vorhanden:
uint8_t* ptr = (uint8_t*)0x124;
ptr[0] = 0xFF;
Hier wandeln wir die Zahl 0x124
in einen Zeiger auf vorzeichenlose 8-Bit-Ganzzahlen (oder Byte) um. Dadurch wird die Variable ptr
effektiv in ein Array umgewandelt, das an der Speicheradresse 0x124
beginnt und das wir wie jedes andere Array verwenden können. So können wir auf einzelne Byte zum Lesen und Schreiben zugreifen. In unserem Fall betrachten wir einen RGBA-Puffer eines Bildes, den wir neu anordnen möchten, um eine Drehung zu erreichen. Um ein Pixel zu verschieben, müssen wir tatsächlich vier aufeinanderfolgende Byte gleichzeitig verschieben (ein Byte für jeden Kanal: R, G, B und A). Um dies zu vereinfachen, können wir ein Array aus vorzeichenlosen 32-Bit-Ganzzahlen erstellen. Unser Eingabe-Image beginnt standardmäßig bei Adresse 4 und unser Ausgabe-Image direkt nach dem Ende des Eingabe-Image:
int bpp = 4;
int imageSize = inputWidth * inputHeight * bpp;
uint32_t* inBuffer = (uint32_t*) 4;
uint32_t* outBuffer = (uint32_t*) (inBuffer + imageSize);
for (int d2 = d2Start; d2 >= 0 && d2 < d2Limit; d2 += d2Advance) {
for (int d1 = d1Start; d1 >= 0 && d1 < d1Limit; d1 += d1Advance) {
int in_idx = ((d1 * d1Multiplier) + (d2 * d2Multiplier));
outBuffer[i] = inBuffer[in_idx];
i += 1;
}
}
Nachdem wir die gesamte JavaScript-Funktion nach C portiert haben, können wir die C-Datei mit emcc
kompilieren:
$ emcc -O3 -s ALLOW_MEMORY_GROWTH=1 -o c.js rotate.c
Wie immer generiert Emscripten eine Glue-Code-Datei namens c.js
und ein WASM-Modul namens c.wasm
. Das WASM-Modul wird auf etwa 260 Byte komprimiert, der Glue-Code auf etwa 3,5 KB. Nach einigen Anpassungen konnten wir den Glue-Code entfernen und die WebAssembly-Module mit den Standard-APIs instanziieren.
Das ist mit Emscripten oft möglich, solange Sie nichts aus der C-Standardbibliothek verwenden.
Rust
Rust ist eine neue, moderne Programmiersprache mit einem umfangreichen Typsystem, ohne Laufzeit und mit einem Besitzmodell, das Speichersicherheit und Threadsicherheit garantiert. Rust unterstützt WebAssembly als Kernfunktion und das Rust-Team hat viel hervorragende Software für das WebAssembly-Ökosystem beigetragen.
Eines dieser Tools ist wasm-pack
von der rustwasm-Arbeitsgruppe. wasm-pack
nimmt Ihren Code und wandelt ihn in ein webfreundliches Modul um, das sofort mit Bundlern wie webpack funktioniert. wasm-pack
ist eine äußerst praktische Funktion, die derzeit jedoch nur für Rust funktioniert. Die Gruppe erwägt, Unterstützung für andere Sprachen hinzuzufügen, die auf WebAssembly ausgerichtet sind.
In Rust sind Slices das, was Arrays in C sind. Genau wie in C müssen wir Slices erstellen, die unsere Startadressen verwenden. Das widerspricht dem von Rust erzwungenen Speichersicherheitsmodell. Um das zu erreichen, müssen wir das Schlüsselwort unsafe
verwenden, mit dem wir Code schreiben können, der nicht diesem Modell entspricht.
let imageSize = (inputWidth * inputHeight) as usize;
let inBuffer: &mut [u32];
let outBuffer: &mut [u32];
unsafe {
inBuffer = slice::from_raw_parts_mut::<u32>(4 as *mut u32, imageSize);
outBuffer = slice::from_raw_parts_mut::<u32>((imageSize * 4 + 4) as *mut u32, imageSize);
}
for d2 in 0..d2Limit {
for d1 in 0..d1Limit {
let in_idx = (d1Start + d1 * d1Advance) * d1Multiplier + (d2Start + d2 * d2Advance) * d2Multiplier;
outBuffer[i as usize] = inBuffer[in_idx as usize];
i += 1;
}
}
Rust-Dateien kompilieren mit
$ wasm-pack build
ergibt ein 7,6 KB großes WASM-Modul mit etwa 100 Byte Glue-Code (beides nach der Gzip-Komprimierung).
AssemblyScript
AssemblyScript ist ein relativ junges Projekt, das als TypeScript-zu-WebAssembly-Compiler dienen soll. Es ist jedoch wichtig zu beachten, dass nicht einfach beliebiger TypeScript-Code verwendet wird. AssemblyScript verwendet dieselbe Syntax wie TypeScript, tauscht jedoch die Standardbibliothek gegen eine eigene aus. Die Standardbibliothek bildet die Funktionen von WebAssembly ab. Das bedeutet, dass Sie nicht einfach beliebigen TypeScript-Code in WebAssembly kompilieren können. Es bedeutet aber auch, dass Sie keine neue Programmiersprache lernen müssen, um WebAssembly zu schreiben.
for (let d2 = d2Start; d2 >= 0 && d2 < d2Limit; d2 += d2Advance) {
for (let d1 = d1Start; d1 >= 0 && d1 < d1Limit; d1 += d1Advance) {
let in_idx = ((d1 * d1Multiplier) + (d2 * d2Multiplier));
store<u32>(offset + i * 4 + 4, load<u32>(in_idx * 4 + 4));
i += 1;
}
}
Angesichts der geringen Typoberfläche unserer rotate()
-Funktion war es relativ einfach, diesen Code zu AssemblyScript zu portieren. Die Funktionen load<T>(ptr:
usize)
und store<T>(ptr: usize, value: T)
werden von AssemblyScript bereitgestellt, um auf den Rohspeicher zuzugreifen. Um unsere AssemblyScript-Datei zu kompilieren, müssen wir nur das npm-Paket AssemblyScript/assemblyscript
installieren und
$ asc rotate.ts -b assemblyscript.wasm --validate -O3
AssemblyScript stellt uns ein etwa 300 Byte großes WASM-Modul und keinen Glue-Code zur Verfügung. Das Modul funktioniert nur mit den WebAssembly-Standard-APIs.
WebAssembly-Forensik
Die 7,6 KB von Rust sind im Vergleich zu den beiden anderen Sprachen überraschend groß. Es gibt einige Tools im WebAssembly-Ökosystem, mit denen Sie Ihre WebAssembly-Dateien analysieren können (unabhängig von der Sprache, mit der sie erstellt wurden). Sie können Ihnen helfen, die Situation zu verbessern.
Twiggy
Twiggy ist ein weiteres Tool des WebAssembly-Teams von Rust, mit dem sich eine Vielzahl von aussagekräftigen Daten aus einem WebAssembly-Modul extrahieren lassen. Das Tool ist nicht Rust-spezifisch und ermöglicht es Ihnen, Dinge wie den Aufrufgraphen des Moduls zu untersuchen, ungenutzte oder überflüssige Abschnitte zu ermitteln und herauszufinden, welche Abschnitte zur Gesamtdateigröße Ihres Moduls beitragen. Letzteres kann mit dem Twiggy-Befehl top
erfolgen:
$ twiggy top rotate_bg.wasm

In diesem Fall sehen wir, dass der Großteil der Dateigröße vom Zuweisungsprogramm stammt. Das war überraschend, da in unserem Code keine dynamischen Zuweisungen verwendet werden. Ein weiterer wichtiger Faktor ist der Unterabschnitt „Funktionsnamen“.
wasm-strip
wasm-strip
ist ein Tool aus dem WebAssembly Binary Toolkit, kurz wabt. Es enthält einige Tools, mit denen Sie WebAssembly-Module untersuchen und bearbeiten können.
wasm2wat
ist ein Disassembler, der ein binäres WASM-Modul in ein für Menschen lesbares Format umwandelt. Wabt enthält auch wat2wasm
, mit dem Sie dieses für Menschen lesbare Format wieder in ein binäres WASM-Modul umwandeln können. Wir haben zwar diese beiden ergänzenden Tools verwendet, um unsere WebAssembly-Dateien zu untersuchen, aber wasm-strip
hat sich als das nützlichste erwiesen. Mit wasm-strip
werden unnötige Abschnitte und Metadaten aus einem WebAssembly-Modul entfernt:
$ wasm-strip rotate_bg.wasm
Dadurch wird die Dateigröße des Rust-Moduls von 7,5 KB auf 6,6 KB reduziert (nach der Gzip-Komprimierung).
wasm-opt
wasm-opt
ist ein Tool von Binaryen.
Es nimmt ein WebAssembly-Modul und versucht, es nur auf der Grundlage des Bytecodes sowohl für die Größe als auch für die Leistung zu optimieren. Einige Tools wie Emscripten führen dieses Tool bereits aus, andere nicht. Es empfiehlt sich in der Regel, mit diesen Tools einige zusätzliche Byte zu sparen.
wasm-opt -O3 -o rotate_bg_opt.wasm rotate_bg.wasm
Mit wasm-opt
können wir noch einige Bytes einsparen, sodass nach der Gzip-Komprimierung insgesamt 6,2 KB übrig bleiben.
#![no_std]
Nach einigen Beratungen und Recherchen haben wir unseren Rust-Code neu geschrieben, ohne die Standardbibliothek von Rust zu verwenden, und dabei das Feature #![no_std]
genutzt. Dadurch werden auch dynamische Speicherzuweisungen vollständig deaktiviert und der Zuweisungscode aus unserem Modul entfernt. Kompilieren dieser Rust-Datei mit
$ rustc --target=wasm32-unknown-unknown -C opt-level=3 -o rust.wasm rotate.rs
ergab nach wasm-opt
, wasm-strip
und gzip ein 1,6 KB großes WASM-Modul. Es ist zwar immer noch größer als die von C und AssemblyScript generierten Module, aber klein genug, um als schlank zu gelten.
Leistung
Bevor wir allein aufgrund der Dateigröße voreilige Schlüsse ziehen, möchten wir noch einmal betonen, dass es bei diesem Projekt um die Optimierung der Leistung ging, nicht der Dateigröße. Wie haben wir die Leistung gemessen und was waren die Ergebnisse?
Benchmarks erstellen
Obwohl WebAssembly ein Low-Level-Bytecode-Format ist, muss es dennoch durch einen Compiler gesendet werden, um hostspezifischen Maschinencode zu generieren. Wie bei JavaScript arbeitet der Compiler in mehreren Phasen. Einfach gesagt: Die erste Phase ist viel schneller beim Kompilieren, generiert aber in der Regel langsameren Code. Sobald das Modul ausgeführt wird, beobachtet der Browser, welche Teile häufig verwendet werden, und sendet diese über einen optimierenden, aber langsameren Compiler.
Unser Anwendungsfall ist insofern interessant, als der Code zum Drehen eines Bildes nur einmal oder vielleicht zweimal verwendet wird. In den meisten Fällen werden wir also nie von den Vorteilen des optimierenden Compilers profitieren. Das ist wichtig, wenn Sie Benchmarks erstellen. Wenn wir unsere WebAssembly-Module 10.000 Mal in einer Schleife ausführen,erhalten wir unrealistische Ergebnisse. Um realistische Zahlen zu erhalten, sollten wir das Modul einmal ausführen und Entscheidungen auf Grundlage der Zahlen aus diesem einzelnen Lauf treffen.
Leistungsvergleich
Diese beiden Diagramme stellen dieselben Daten unterschiedlich dar. Im ersten Diagramm vergleichen wir nach Browser, im zweiten nach verwendeter Sprache. Ich habe eine logarithmische Zeitskala gewählt. Außerdem ist es wichtig, dass für alle Benchmarks dasselbe 16-Megapixel-Testbild und derselbe Hostcomputer verwendet wurden. Eine Ausnahme bildete ein Browser, der nicht auf demselben Computer ausgeführt werden konnte.
Ohne diese Grafiken zu sehr zu analysieren, ist klar, dass wir unser ursprüngliches Leistungsproblem gelöst haben: Alle WebAssembly-Module werden in etwa 500 ms oder weniger ausgeführt. Das bestätigt, was wir eingangs gesagt haben: WebAssembly bietet eine vorhersehbare Leistung. Unabhängig davon, welche Sprache wir auswählen, ist die Varianz zwischen Browsern und Sprachen minimal. Genauer gesagt: Die Standardabweichung von JavaScript in allen Browsern beträgt etwa 400 ms, während die Standardabweichung aller unserer WebAssembly-Module in allen Browsern etwa 80 ms beträgt.
Aufwand
Ein weiterer Messwert ist der Aufwand, der für die Erstellung und Integration unseres WebAssembly-Moduls in Squoosh erforderlich war. Es ist schwierig, dem Aufwand einen numerischen Wert zuzuweisen. Daher werde ich keine Grafiken erstellen, aber es gibt ein paar Dinge, die ich anmerken möchte:
AssemblyScript war reibungslos. Damit kann ich nicht nur TypeScript verwenden, um WebAssembly zu schreiben, was die Codeüberprüfung für meine Kollegen sehr einfach macht, sondern es werden auch WebAssembly-Module ohne Kleber erzeugt, die sehr klein sind und eine anständige Leistung bieten. Die Tools im TypeScript-Ökosystem, wie Prettier und TSLint, funktionieren wahrscheinlich einfach.
Rust in Kombination mit wasm-pack
ist ebenfalls sehr praktisch, eignet sich aber besser für größere WebAssembly-Projekte, bei denen Bindungen und Speicherverwaltung erforderlich sind. Wir mussten etwas vom Happy Path abweichen, um eine wettbewerbsfähige Dateigröße zu erreichen.
Mit C und Emscripten wurde ein sehr kleines und leistungsstarkes WebAssembly-Modul erstellt, aber ohne den Mut, in den Glue-Code einzutauchen und ihn auf das Wesentliche zu reduzieren, ist die Gesamtgröße (WebAssembly-Modul + Glue-Code) am Ende recht groß.
Fazit
Welche Sprache sollten Sie also verwenden, wenn Sie einen JS-Hotpath haben und ihn mit WebAssembly schneller oder konsistenter machen möchten? Wie immer bei Leistungsfragen lautet die Antwort: Das hängt davon ab. Was haben wir also veröffentlicht?
Wenn wir die Kompromisse zwischen Modulgröße und Leistung der verschiedenen verwendeten Sprachen vergleichen, scheint C oder AssemblyScript die beste Wahl zu sein. Wir haben uns entschieden, Rust zu versenden. Dafür gibt es mehrere Gründe: Alle bisher in Squoosh enthaltenen Codecs werden mit Emscripten kompiliert. Wir wollten unser Wissen über das WebAssembly-Ökosystem erweitern und eine andere Sprache in der Produktion verwenden. AssemblyScript ist eine gute Alternative, aber das Projekt ist relativ jung und der Compiler ist nicht so ausgereift wie der Rust-Compiler.
Der Unterschied in der Dateigröße zwischen Rust und den anderen Sprachen sieht im Streudiagramm zwar ziemlich drastisch aus, ist in der Realität aber nicht so groß: Das Laden von 500 B oder 1,6 KB dauert selbst über 2G weniger als eine Zehntelsekunde. Und Rust wird hoffentlich bald die Lücke in Bezug auf die Modulgröße schließen.
In Bezug auf die Laufzeitleistung ist Rust im Durchschnitt in Browsern schneller als AssemblyScript. Insbesondere bei größeren Projekten ist es wahrscheinlicher, dass Rust schnelleren Code ohne manuelle Codeoptimierungen erzeugt. Das sollte Sie aber nicht davon abhalten, das zu verwenden, womit Sie sich am wohlsten fühlen.
Alles in allem: AssemblyScript war eine tolle Entdeckung. So können Webentwickler WebAssembly-Module erstellen, ohne eine neue Sprache lernen zu müssen. Das AssemblyScript-Team war sehr reaktionsschnell und arbeitet aktiv an der Verbesserung seiner Toolchain. Wir werden AssemblyScript in Zukunft auf jeden Fall im Auge behalten.
Update: Rust
Nach der Veröffentlichung dieses Artikels hat uns Nick Fitzgerald vom Rust-Team auf ihr hervorragendes Rust Wasm-Buch hingewiesen, das einen Abschnitt zur Optimierung der Dateigröße enthält. Wenn wir die dortigen Anweisungen befolgten (insbesondere die Aktivierung der Link-Time-Optimierungen und die manuelle Panic-Verarbeitung), konnten wir „normalen“ Rust-Code schreiben und wieder Cargo
(das npm
von Rust) verwenden, ohne die Dateigröße zu erhöhen. Das Rust-Modul hat nach der Gzip-Komprimierung eine Größe von 370 Byte. Weitere Informationen finden Sie im von mir auf Squoosh geöffneten PR.
Besonderer Dank gilt Ashley Williams, Steve Klabnik, Nick Fitzgerald und Max Graey für ihre Unterstützung.