WebAssembly runtimes keep improving every day and quite a few things have changed since the last time I benchmarked WebAssembly runtimes using libsodium.

So maybe now is a good time for a new run.

People suggested that wasm-opt from binaryen should be run before compiling WebAssembly code. This step was thus added.

This benchmark can be reproduced using the dist-build/wasm32-wasi.sh --bench command from libsodium 1.0.18-stable.

Compared runtimes:

WAVM 2019-10-21 (default configuration)

Lucet 0.3.0 (with –opt-level 2)

Wasmtime 0.2.0 (with optimizations turned on)

Wasmer 0.8.0 and 0.9.0 (Singlepass/Cranelift/LLVM backends)

Results:

WAVM beats everything else, hands down.

Wasmer’s singlepass backend compiles quickly, but, as expected, is consistently way slower than other backends and runtimes.

backend compiles quickly, but, as expected, is consistently way slower than other backends and runtimes. Even though Wasmtime, Lucet and Wasmer/Cranelift use the same code generator (Cranelift), their performance is not identical. Wasmtime is the slowest, followed by Lucet neck-to-neck with Wasmer. Different versions of Cranelift and different WASI implementations may explain the difference.

I was expecting Wasmer with its LLVM backend to perform the same as WAVM, also based on LLVM. Before version 0.9, Wasmer was noticeably slower, but they quickly found and addressed the issue. It’s now pretty close to WAVM, although the latter remains the fastest runtime to date.

WAVM is not only a speed daemon, but it is also the most featureful implementation yet, fully supporting WebAssembly 1.0, WASI, and more extensions than any other runtime (SIMD, threads, reference types, exceptions, sign extension, multiple memories…)

It is also quite portable, and comes with an extensive C and C++ API to be easily embedded in other projects. Its development pace is also quite impressive.

Test lucet native wasmer wasmer-llvm wasmer-singlepass wasmer09-llvm wasmtime wavm aead_chacha20poly1305 50.68 17.66 52.68 57.74 100.0 34.52 56.84 25.01 aead_chacha20poly13052 51.9 21.27 56.76 58.7 100.0 33.44 54.22 26.81 aead_xchacha20poly1305 43.29 18.24 44.77 47.83 100.0 33.23 46.23 23.15 auth 58.19 13.94 51.32 40.43 100.0 30.93 57.62 17.69 auth2 60.55 18.59 72.86 46.23 100.0 37.06 67.21 21.11 auth3 77.19 19.61 65.79 47.56 100.0 39.97 69.8 21.8 auth5 61.82 17.32 58.98 43.61 100.0 32.16 66.07 22.48 auth6 56.26 28.91 67.22 44.94 100.0 44.29 67.68 36.65 auth7 54.81 17.32 58.92 51.92 100.0 33.05 67.62 21.01 box 61.35 24.33 75.08 55.27 100.0 42.55 71.76 32.02 box2 43.67 15.02 44.35 39.35 63.88 27.35 100.0 20.63 box7 62.14 11.76 69.53 54.42 100.0 42.56 78.37 32.69 box8 61.09 11.7 68.11 53.53 100.0 42.28 72.2 32.24 box_easy 61.3 11.68 71.99 53.05 100.0 42.42 70.63 32.27 box_easy2 50.25 11.86 62.67 55.21 100.0 49.02 65.63 31.18 box_seal 62.32 15.15 73.48 51.39 100.0 40.3 76.2 30.29 box_seed 85.53 25.57 74.55 52.58 100.0 41.89 76.41 28.12 chacha20 100.0 34.54 98.8 100.0 FAIL 61.89 96.21 55.55 codecs 62.4 28.16 62.47 55.39 100.0 38.23 60.43 31.23 core1 64.52 58.06 58.06 58.06 100.0 51.61 54.84 45.16 core2 65.62 50.0 53.12 59.38 100.0 46.88 53.12 43.75 core3 55.23 20.87 64.18 43.18 100.0 35.92 66.21 25.26 core4 45.45 14.05 15.7 100.0 50.41 12.4 15.7 11.57 core5 90.62 56.25 56.25 84.38 100.0 43.75 56.25 43.75 core6 51.35 100.0 51.35 62.16 94.59 43.24 94.59 37.84 core_ed25519 60.41 17.45 66.05 54.28 100.0 41.59 69.34 33.44 core_ristretto255 61.25 18.49 67.56 55.1 100.0 42.78 70.09 33.04 ed25519_convert 63.7 17.26 70.9 54.26 100.0 42.6 74.24 31.19 generichash 49.32 15.7 53.74 51.27 100.0 32.09 59.02 24.11 generichash2 60.56 9.39 56.43 39.11 100.0 31.0 64.36 18.87 generichash3 43.3 8.57 50.28 37.04 100.0 28.96 53.18 17.33 hash 36.57 11.37 100.0 26.29 63.84 31.97 40.9 12.91 hash3 60.38 20.38 81.51 38.49 100.0 36.98 65.66 19.25 kdf 49.0 10.3 52.21 43.18 100.0 31.82 62.91 19.62 keygen 43.72 20.82 42.83 41.16 100.0 26.96 44.63 19.08 kx 61.74 16.01 83.51 53.01 100.0 42.23 72.26 32.83 metamorphic 55.01 16.99 60.13 46.22 100.0 34.07 61.75 24.02 onetimeauth 56.46 40.82 85.71 84.35 100.0 63.27 59.18 29.93 onetimeauth7 54.85 32.67 56.73 57.68 100.0 40.16 59.71 35.51 pwhash_argon2i 33.55 7.83 54.59 48.62 100.0 35.65 56.24 23.58 pwhash_argon2id 34.2 8.25 56.19 50.58 100.0 35.69 58.47 23.2 pwhash_scrypt 48.95 12.08 58.7 42.76 100.0 44.77 65.75 25.38 pwhash_scrypt_ll 48.33 12.87 59.23 42.54 100.0 44.57 66.02 25.52 randombytes 60.79 24.91 73.75 68.91 100.0 47.43 76.69 42.17 scalarmult 68.74 20.26 69.25 51.67 100.0 43.26 72.77 30.28 scalarmult2 67.57 25.68 72.97 50.38 100.0 41.94 80.5 28.28 scalarmult5 63.56 12.17 71.6 55.31 100.0 44.59 74.73 33.27 scalarmult6 62.56 12.19 70.3 54.95 100.0 42.47 69.93 34.82 scalarmult7 63.78 11.96 69.79 54.34 100.0 44.03 72.6 32.58 scalarmult8 62.61 18.24 67.83 54.28 100.0 42.45 71.44 32.5 scalarmult_ed25519 61.73 15.05 71.44 53.44 100.0 42.32 73.14 32.38 scalarmult_ristretto255 62.49 14.45 70.45 52.42 100.0 41.73 74.41 30.68 secretbox 51.36 26.89 70.09 65.26 100.0 43.96 58.01 33.08 secretbox2 29.99 15.22 28.95 32.76 100.0 24.68 29.18 15.22 secretbox7 51.9 16.02 55.52 56.37 100.0 37.45 58.1 31.02 secretbox8 53.14 38.03 56.43 58.82 100.0 39.44 58.66 34.84 secretbox_easy 55.1 23.9 56.36 57.67 100.0 37.83 64.6 46.08 secretbox_easy2 66.65 24.02 56.55 56.52 100.0 46.69 54.22 48.52 secretstream_xchacha20poly1305 89.79 31.7 96.58 100.0 FAIL 63.66 95.61 55.95 shorthash 44.2 34.98 54.44 62.63 100.0 42.32 45.39 32.94 siphashx24 51.06 34.89 48.23 64.54 100.0 36.88 43.26 35.6 sodium_utils 42.92 25.94 49.19 68.04 100.0 32.74 49.97 28.03 stream 55.44 17.01 62.42 45.77 100.0 37.0 66.03 26.29 stream2 54.48 20.16 62.28 42.64 100.0 35.7 65.33 24.53 stream3 49.07 47.22 69.44 51.85 100.0 52.78 64.81 31.48 stream4 49.1 24.55 74.73 55.96 100.0 49.46 51.62 27.08 verify1 39.32 14.05 45.45 38.39 100.0 28.03 53.47 22.78 xchacha20 78.6 26.12 90.47 72.59 FAIL 54.69 100.0 42.07

Download more readable results as a PDF file here: WebAssembly benchmark

If speed is your primary concern, choosing WAVM should be a no brainer.

On the other hand, Wasmer’s ecosystem is second to none, already providing excellent libraries for many programming languages and applications (PostgreSQL!) and a well-designed package system.

Wasmtime can generate debugging information, ready to use with LLVM. This makes it a great choice for development.

And Lucet was designed to run multiple instances on the same thread. To do so, it provides a way for an application to pause (yield), so that it can be resumed later by the host. That feature seems to be missing from other runtimes.