Performance of WebAssembly Runtimes in 2026

theanonymousone1 pts0 comments

Performance of WebAssembly runtimes in 2026 - Frank DENIS random thoughts.

Skip to main content

I wanted to know if WebAssembly runtimes are getting faster.

This is a follow-up to the earlier libsodium WebAssembly benchmarks from 2019, 2021 and 2023.

Not “does the newest version beat native code in one microbenchmark?”, and not “which runtime has the prettiest benchmark chart?”, but something more boring and more useful:

If I take the same C crypto code, compile it to WebAssembly, and run it on the latest runtime, a runtime from one year ago, and a runtime from two years ago, are things actually improving?

So I benchmarked libsodium on WebAssembly runtimes released around June 2024, June 2025, and June 2026.

The short version:

WAVM and WasmEdge can be very fast. WasmEdge 0.17.0 needed an explicit --run-mode=aot; without it, the compiled modules ran like interpreter-mode Wasm.

WAMR in AOT mode is also very fast, landing right next to WAVM and the best Wasmtime results.

wasm2c, Wasmer, and Wasmtime are all close enough to native to be interesting for CPU-bound crypto.

Wazero is slower, but stable.

The Node and Bun rows need a full rerun with longer benchmark loops. A smoke test showed that the short-loop run substantially under-warmed the JITs.

The experimental WebAssembly wide_arithmetic instructions are a big deal for crypto code when runtimes support them.

What I measured

The test program is libsodium’s benchmark suite, built from libsodium commit 8e3be8615ba6adcd7babaecf5e76f516890ba5fb.

I built one native baseline and several WebAssembly variants:

native x86-64, compiled with Zig using the local CPU target

plain WebAssembly

WebAssembly with lime1

WebAssembly with lime1 and simd128

WebAssembly with lime1, simd128, and wide_arithmetic

For the native reference, libsodium was built with -Dcpu=native. For wasm2c, the generated C was compiled with zig cc -O3 -march=native.

For WAMR, I used AOT mode: wamrc compiled each .wasm file to an .aot file, and iwasm ran the resulting AOT file. wamrc doesn’t accept --cpu=native, so I used --target=x86_64 --cpu=x86-64-v4 --opt-level=3, which matches the host’s available x86-64 feature level and works across the WAMR versions that could compile these modules.

The native command was:

zig build -Denable_benchmarks -Doptimize=ReleaseFast -Dcpu=native -Diterations=3

The WebAssembly commands were the same shape, with a wasm32-wasi target and the feature-specific CPU strings:

zig build -Denable_benchmarks -Dtarget=wasm32-wasi -Doptimize=ReleaseFast -Diterations=3<br>zig build -Denable_benchmarks -Dtarget=wasm32-wasi -Doptimize=ReleaseFast -Dcpu=lime1 -Diterations=3<br>zig build -Denable_benchmarks -Dtarget=wasm32-wasi -Doptimize=ReleaseFast -Dcpu=lime1+simd128 -Diterations=3<br>zig build -Denable_benchmarks -Dtarget=wasm32-wasi -Doptimize=ReleaseFast -Dcpu=lime1+simd128+wide_arithmetic -Diterations=3

The host was an AMD Ryzen AI 9 HX 470 with 12 cores and 24 threads. CPU boost was disabled and the maximum CPU frequency was 2 GHz. The OS was Linux 7.1.0-rc7, and Zig was 0.17.0-dev.948+e949341b7.

The numbers below are the geometric mean of per-benchmark slowdowns relative to the native build. Lower is better. A value of 2.0 means “twice as slow as native” on this machine.

I used ITERATIONS=3, so the very small libsodium tests are noisy and quantized. Rows reporting zero time were excluded from the aggregate. I did not pin benchmark processes to specific cores. This is still useful for comparing broad runtime behavior, but don’t treat the last decimal place as meaningful.

Versions

For every runtime except WAVM, I used the latest stable release available on June 23, 2026, plus a stable release from roughly one year earlier and one from roughly two years earlier.

Runtime<br>2024<br>2025<br>2026

Bun<br>1.1.16<br>1.2.17<br>1.3.14

Node<br>22.3.0<br>24.2.0<br>26.3.1

WAMR<br>2.1.0<br>2.3.1<br>2.4.4

WABT wasm2c<br>1.0.35<br>1.0.37<br>1.0.41

WasmEdge<br>0.14.0<br>0.14.1<br>0.17.0

Wasmer<br>4.3.2<br>6.0.1<br>7.1.0

Wasmtime<br>22.0.0<br>34.0.0<br>46.0.0

WAVM<br>n/a<br>n/a<br>nightly/2026-04-05

Wazero<br>1.7.3<br>1.9.0<br>1.12.0

WAVM is awkward to compare historically. The old available nightly collapsed to a 2022 binary for both the 2024 and 2025 slots, and that binary refused to run on this machine. I only kept the 2026 nightly.

WAMR 2.1.0, the selected 2024 release, installed fine but its AOT compiler failed on these Zig-generated modules with invalid WASM stack data type. I kept the version in the matrix, but did not include an aggregate for it.

Baseline WebAssembly

This is the plain WebAssembly build, without lime1, SIMD, or wide arithmetic.

Runtime<br>2024<br>2025<br>2026

WAVM<br>n/a<br>n/a<br>1.41

WAMR AOT<br>n/a<br>1.59<br>1.57

WasmEdge<br>1.66<br>1.98<br>1.74

wasm2c<br>2.01<br>2.08<br>1.86

Wasmer<br>2.13<br>2.56<br>2.08

Wasmtime<br>2.67<br>2.54<br>2.41

Wazero<br>4.84<br>4.70<br>4.72

Node<br>8.60<br>8.22<br>7.95

Bun<br>27.41<br>26.42<br>8.77

There isn’t one universal trend.

Wasmtime steadily improved: 2.67x native in 2024, 2.54x in 2025, 2.41x in 2026. That’s not a revolution, but it is real progress.

Node also...

webassembly native runtime from lime1 build

Related Articles