Prestaties van WebAssembly-runtimes in 2026
Ik wilde weten of WebAssembly-runtimes sneller worden. Dit is een vervolg op de eerdere libsodium WebAssembly-benchmarks uit 2019, 2021 en 2023.
Het doel is niet om te bepalen of de nieuwste versie in één microbenchmark native code verslaat, of welke runtime de mooiste grafiek heeft, maar om iets saaier en nuttiger vast te stellen: als ik dezelfde C-crypto-code neem, deze compileer naar WebAssembly en uitvo op de nieuwste runtime, een runtime van een jaar geleden en een runtime van twee jaar geleden, verbeteren de zaken dan daadwerkelijk?
Daarom heb ik libsodium gebenchmarkt op WebAssembly-runtimes die zijn uitgebracht rond juni 2024, juni 2025 en juni 2026.
De korte samenvatting:
- Wasmer presteert het best, maar WAVM, WAMR en Wasmtime zitten dichtbij.
- WAVM heeft de beste optimizer en is in staat om zeer snelle code te genereren vanuit baseline, portable WebAssembly.
- De nieuwe WebAssembly
wide_arithmetic-instructies zijn van groot belang voor cryptocode wanneer runtimes deze ondersteunen.
Wat is er gemeten?
Het testprogramma is de benchmark-suite van libsodium, gebouwd vanuit libsodium commit 8e3be8615ba6adcd7babaecf5e76f516890ba5fb.
Ik heb één native baseline en verschillende WebAssembly-varianten gebouwd:
- native x86-64, gecompileerd met Zig met het lokale CPU-doel.
- plain WebAssembly.
- WebAssembly met
lime1. - WebAssembly met
lime1ensimd128. - WebAssembly met
lime1,simd128enwide_arithmetic.
Voor de native referentie werd libsodium gebouwd met -Dcpu=native. Voor wasm2c werd de gegenereerde C-code gecompileerd met zig cc -O3 -march=native.
Voor WAMR gebruikte ik de AOT-modus: wamrc compileerde elk .wasm-bestand naar een .aot-bestand, en iwasm voerde het resulterende AOT-bestand uit. Omdat wamrc geen --cpu=native accepteert, gebruikte ik --target=x86_64 --cpu=x86-64-v4 --opt-level=3, wat overeenkomt met het beschikbare x86-64-functieniveau van de host en werkt over de WAMR-versies die deze modules konden compileren.
De native opdracht was: zig build -Denable_benchmarks -Doptimize=ReleaseFast -Dcpu=native -Diterations=3
De WebAssembly-opdrachten hadden dezelfde structuur, met een wasm32-wasi target en functie-specifieke CPU-strings:
zig build -Denable_benchmarks -Dtarget=wasm32-wasi -Doptimize=ReleaseFast -Diterations=3zig build -Denable_benchmarks -Dtarget=wasm32-wasi -Doptimize=ReleaseFast -Dcpu=lime1 -Diterations=3zig build -Denable_benchmarks -Dtarget=wasm32-wasi -Doptimize=ReleaseFast -Dcpu=lime1+simd128 -Diterations=3zig build -Denablebenchmarks -Dtarget=wasm32-wasi -Doptimize=ReleaseFast -Dcpu=lime1+simd128+widearithmetic -Diterations=3
De host was een AMD Ryzen AI 9 HX 470 met 12 cores en 24 threads. CPU-boost was uitgeschakeld en de maximale CPU-frequentie was 2 GHz. Het besturingssysteem was Linux 7.1.0-rc7, en Zig was versie 0.17.0-dev.948+e949341b7.
De onderstaande cijfers zijn het geometrisch gemiddelde van de vertragingen per benchmark ten opzichte van de native build. Lager is beter. Een waarde van 2.0 betekent "twee keer zo traag als native" op deze machine.
Ik gebruikte ITERATIONS=3, waardoor de zeer kleine libsodium-tests ruis vertonen en gekwantiseerd zijn. Rijen waarin nul tijd werd gerapporteerd, zijn uitgesloten van het aggregaat.
Versies
Voor elke runtime, behalve WAVM, heb ik de nieuwste stabiele release gebruikt die beschikbaar was op 23 juni 2026, plus een stabiele release van ongeveer een jaar eerder en een van ongeveer twee jaar eerder.
| Runtime | 2024 | 2025 | 2026 |
|---|---|---|---|
| Bun | 1.1.16 | 1.2.17 | 1.3.14 |
| Node | 22.3.0 | 24.2.0 | 26.3.1 |
| WAMR | 2.1.0 | 2.3.1 | 2.4.4 |
| WABT wasm2c | 1.0.35 | 1.0.37 | 1.0.41 |
| WasmEdge | 0.14.0 | 0.14.1 | 0.17.0 |
| Wasmer | 4.3.2 | 6.0.1 | 7.1.0 |
| Wasmtime | 22.0.0 | 34.0.0 | 46.0.0 |
| WAVM | n/a | n/a | nightly/2026-04-05 |
| Wazero | 1.7.3 | 1.9.0 | 1.12.0 |
WAVM is lastig historisch te vergelijken. De oude beschikbare nightly-versie reduceerde tot een binary uit 2022 voor zowel de slots van 2024 als 2025, en die binary weigerde op deze machine te draaien. Ik heb alleen de nightly van 2026 behouden.
WAMR 2.1.0, de geselecteerde release uit 2024, kon wel worden geïnstalleerd, maar de AOT-compiler faalde bij deze door Zig gegenereerde modules met een invalid WASM stack data type. Ik heb de versie in de matrix laten staan, maar geen aggregaat hiervoor opgenomen.
Baseline WebAssembly
Dit is de gewone WebAssembly-build, zonder lime1, SIMD of wide arithmetic. Er is geen universele trend zichtbaar.
- Wasmtime verbeterde gestaag: 2,67x native in 2024, 2,54x in 2025, 2,41x in 2026. Het werd elk jaar sneller, en de winst bevindt zich in de tienden, boven de ruis.
- Node verbeterde ook langzaam, van 8,60x native naar 7,95x native.
- Wazero was in feite vlak: 4,84x, 4,70x, 4,72x native. Geen echte beweging over twee jaar.
- WAMR in AOT-modus was al snel in 2025 en bleef dat in 2026: 1,59x native, daarna 1,57x native; dit is hetzelfde binnen de ruis van deze benchmark. Ik heb geen volledig WAMR-cijfer voor 2024 omdat WAMR 2.1.0 deze modules niet kon compileren.
- Wasmer verslechterde in de release van 2025 die ik testte, maar herstelde zich in 2026. De baseline van 2026 verslaat die van 2024 nauwelijks.
- wasm2c verbeterde bescheiden in 2026. Het blijft een van de beste opties als ahead-of-time vertaling naar native C acceptabel is voor het implementatiemodel.
- Bun is de uitzondering. De resultaten uit 2024 en 2025 lagen ver achter, maar het resultaat van 2026 is ongeveer drie keer zo snel als dat van 2025. Het is nog steeds trager dan Node in deze benchmark, maar de richting is uitstekend.
- WasmEdge is ook snel, maar het gedrag van de command-line veranderde genoeg om van invloed te zijn. Mijn eerste run met 0.17.0 gebruikte per ongeluk de interpreter-modus voor gecompileerde modules en leek catastrofaal traag. Het uitvoeren van de gecompileerde modules met
--run-mode=aotloste dit op: de baseline van 2026 was 1,74x native, tussen de baseline-resultaten van 2024 en 2025 in.
Beste ondersteunde build per jaar
De baseline-tabel is nuttig omdat hij hetzelfde WebAssembly-doel overal vergelijkt. Maar als je een runtime kiest voor je eigen implementatie, ben je waarschijnlijk geïnteresseerd in de snelste build die die runtime daadwerkelijk kan uitvoeren.
Daarom heb ik voor elke runtime en elk jaar het beste complete resultaat geselecteerd uit de ondersteunde builds: baseline, lime1, lime1+simd128 en lime1+simd128+wide_arithmetic.
De resultaten lijken op de vorige grafiek, behalve voor Wasmtime en Wasmer, die echt profiteren van wide_arithmetic.
CPU-functievarianten
Het verhaal over WebAssembly-functies is interessanter dan het jaar-op-jaar verhaal over runtimes. Voor de releases van 2026 waren dit de aggregate slowdowns:
lime1 en simd128 alleen zijn hier geen wondermiddelen. Soms helpen ze, soms hinderen ze, en soms gaat het verschil verloren in de ruis van de benchmark.
widearithmetic is anders. Alleen Wasmtime en Wasmer konden de volledige widearithmetic-build draaien van de complete stabiele rijen die ik testte. WAMR weigerde deze met unsupported opcode 0xfc13. Maar wanneer wide_arithmetic werkte, was dat de grootste versnelling in het hele experiment:
- Wasmtime 46.0.0: 2,41x native zonder, 1,46x native met.
- Wasmer 7.1.0: 2,08x native zonder, 1,33x native met.
Dit is het soort verandering waar ik van hou. Veel van de dure operaties van libsodium zijn rekenintensief. Als de WebAssembly ISA die rekenkunde direct kan uitdrukken, heeft de runtime veel minder werk om te herontdekken wat de C-compiler al wist.
Fouten en mislukkingen
De meeste runs werden foutloos voltooid, maar niet alle.
- Bun 1.2.17 faalde bij
boxeasyin de baseline-build. Bun 1.1.16 faalde bijpwhashargon2iin delime1enlime1+simd128builds. - Node 22.3.0 faalde oorspronkelijk bij
pwhashargon2i,pwhashargon2idenpwhashscryptin de baseline,lime1enlime1+simd128builds. De fouten werden niet opgelost door de JavaScript heap- of stack-instellingen van Node te verhogen. Ze werden opgelost door de Wasm-modules een expliciet maximum lineair geheugen te geven. Bij de baseline-build zorgde een maximum van 1024 pagina's (64 MiB) ervoor datpwhashargon2i,pwhashargon2idenpwhashscryptvoltooid werden. Maarpwhash_scryptfaalde bij 512 pagina's en veroorzaakte opnieuw een segfault bij 1536 pagina's en hoger, dus dit lijkt een V8-geheugenmodus-drempel te zijn in plaats van een simpele "meer geheugen is beter"-instelling. - WAMR 2.1.0 (de slot voor 2024) kon zelfs de baseline-modules niet compileren in AOT-modus. WAMR 2.3.1 en 2.4.4 compileerden en voerden de baseline,
lime1enlime1+simd128builds uit, maar nietwide_arithmetic.
Deze mislukkingen zijn uitgesloten van het aggregaat, net als benchmark-rijen met een gerapporteerde mediaan van nul.
Worden runtimes sneller?
Sommigen van hen wel.
- Wasmtime is een duidelijk "ja": het werd elk jaar sneller in deze benchmark, elke keer een beetje.
- Node is ook een "ja", maar de stijging is flauw.
- Bun is een luid "ja" tussen 2025 en 2026. Het heeft voor deze workload nog veel in te halen, maar de verbetering is te groot om te negeren.
- Wazero is grotendeels vlak.
- WAMR is ook grotendeels vlak tussen de versies die hier werkten, maar "vlak" op ongeveer 1,4x tot 1,6x native is een zeer goede positie.
- Wasmer is gemengd als je alleen naar de baseline kijkt, maar de release van 2026 die
wide_arithmeticondersteunt, verandert het praktische antwoord voor cryptocode. Met die functie ingeschakeld was het het snelste complete resultaat van 2026 dat ik kon vergelijken over een normale huidige release. - wasm2c blijft goed. Als je WebAssembly vooraf kunt vertalen naar C en dit voor de host kunt compileren, is het moeilijk te verslaan.
- WAVM produceerde het snelste baseline-cijfer voor 2026, maar ik heb geen eerlijke vergelijking voor 2024 of 2025.
- WasmEdge blijft uitstekend zodra het in AOT-modus wordt gedwongen. De per ongeluk uitgevoerde interpreter-modus run was een goede herinnering dat command-line defaults ook onderdeel zijn van de benchmark.
Belangrijkste conclusies
Als je CPU-intensieve cryptografie uitvoert in WebAssembly, maakt de keuze van de runtime nog steeds veel uit. Het verschil tussen het snelste complete huidige resultaat en het traagste huidige resultaat is groot: Wasmer met wide_arithmetic was 1,33x native, terwijl de huidige Bun-baseline 8,77x native was.
Ondersteuning van functies is ook cruciaal. Dezelfde runtime kan bewegen van "best oké" naar "verrassend dicht bij native" wanneer de WebAssembly-module betere rekeninstructies kan gebruiken.
Het troostrijke is dat de mainstream runtimes niet stilstaan. Wasmtime verbeterde gestaag. Bun maakte een enorme sprong. Wasmer kreeg een functie die ertoe doet voor echte crypto-workloads. WasmEdge bleef snel zodra de AOT-run-modus expliciet was.
Het minder troostrijke is dat WebAssembly-prestaties nog steeds geen eenduidig ding zijn. Het hangt af van de runtime, de release, de ingeschakelde WebAssembly-functies, of de code via WASI vanuit JavaScript gaat en of native ahead-of-time compilatie is toegestaan.
Benchmark daarom je eigen werklast. Maar als jouw workload lijkt op libsodium, is het antwoord in 2026: WebAssembly kan dicht bij native komen, wide_arithmetic is de moeite waard om naar te kijken, en ja, sommige runtimes worden echt sneller.
Groetjes,