🤝JavaScript ↔ WebAssembly
Ein Wasm-Modul ist allein machtlos: Alles, was es mit der Außenwelt tun darf, bekommt es als Import. Zurück gibt es Funktionen, Speicher, Tabellen und Globals als Exporte. Alle Beispiele laufen hier live mit der Engine deines Browsers.
🧩Modul, Instanz, Importe, Exporte
new WebAssembly.Memory({ initial: 1 })) und als Import mit einem oder mehreren Modulen teilen.1 · Laden und instanziieren
const bytes = await fetch("demo.wasm").then(r => r.arrayBuffer());
let memory;
const imports = {
env: {
// wird aus Wasm aufgerufen: call $log
log: (ptr, len) => console.log(
new TextDecoder().decode(new Uint8Array(memory.buffer, ptr, len)))
}
};
const { instance } = await WebAssembly.instantiate(bytes, imports);
memory = instance.exports.memory;
instance.exports.add(20, 22); // 42
instance.exports.hallo(); // log(16, 12) → "Hallo, Wasm!"Im Web empfiehlt sich WebAssembly.instantiateStreaming(fetch(url), imports) – kompiliert schon während des Downloads (Server muss Content-Type: application/wasm senden).
Live im Browser: Demo-Modul (341 Byte)
Tipp: factorial(13) liefert 1932053504 – i32 rechnet modulo 2³², 13! = 6227020800 passt nicht hinein.
2 · Strings über den linearen Speicher
Wasm-Funktionen kennen nur Zahlen. Ein String wird von JavaScript als UTF-8 in den Speicher geschrieben; übergeben werden Zeiger und Länge. Danach liest JavaScript die veränderten Bytes wieder aus.
const bytes = new TextEncoder().encode(text); // UTF-8 new Uint8Array(memory.buffer).set(bytes, 256); // in den Speicher instance.exports.grossschreiben(256, bytes.length); const out = new Uint8Array(memory.buffer, 256, bytes.length); new TextDecoder().decode(out);
3 · Tabellen und call_indirect
Funktionszeiger gibt es in Wasm nicht als Speicheradresse, sondern als Index in eine Tabelle mit Funktionsreferenzen.call_indirect prüft zur Laufzeit, ob die Signatur passt – ein Sprung auf beliebigen Code ist unmöglich.
(table (export "tabelle") 3 funcref) (elem (i32.const 0) $add $sub $mul) … local.get $a local.get $b local.get $op call_indirect (type $binop) // JavaScript sieht dieselbe Tabelle: instance.exports.tabelle.get(2)(6, 7); // 42
Tabellengröße: … · Index außerhalb → WebAssembly.RuntimeError (Trap).
4 · Performance: gleiche Funktion in JS und Wasm
Beide Varianten nutzen denselben Algorithmus (siehe benchmark.wat). Gemessen wird hier in deinem Browser: Median aus 7 Läufen nach einem Aufwärmlauf.
- Moderne JS-Engines optimieren solche Ganzzahl-Schleifen per JIT sehr gut – oft liegen JS und Wasm nahe beieinander, manchmal ist JS schneller.
- Browser vergröbern
performance.now()absichtlich (Schutz vor Timing-Angriffen); sehr kurze Messungen sind ungenau. - Stärken von Wasm liegen eher bei vorhersagbarer Leistung ohne Aufwärmphase, portiertem C/C++/Rust-Code, SIMD, Threads und kompakten Datenlayouts im linearen Speicher.
- Jeder Aufruf über die JS↔Wasm-Grenze kostet etwas – viele winzige Aufrufe können den Vorteil aufzehren.
Noch nicht gemessen. Die Messung blockiert den Browser kurz.