Skip to main content

Performance

This page compares WaaS with other scripting engines and WebAssembly runtimes. The results can be reproduced with WaaS.Benchmarks.

Interpreter backends​

BackendUsed when
BurstUnity with com.unity.burst installed, on the platforms that Burst compiles for
ManagedOtherwise: .NET, and Unity without Burst

No change of code is needed to use the Burst backend. A debugger uses a slower interpreter that executes each instruction separately.

Without Burst, IL2CPP gains less from the optimizations of the interpreter than CoreCLR does. If performance matters on IL2CPP, install com.unity.burst.

Conditions​

Windows 11, Intel Core i7-13700F. Unity 6000.5.8f1 with non-development IL2CPP players, .NET 10, xLua (59bf426, Lua 5.3), Lua-CSharp 0.5.8, wasmtime 50.0.1 and wasm3 (28ecb9a, built with clang). WaaS is built from the commit that added these results. Each value is the lowest of the medians of four alternating runs.

The same workloads are implemented in Rust, MoonBit and Lua, and the harnesses verify that all of them compute the same checksums. wasm3 and wasmtime run outside of .NET, so only the compute workloads are measured on them.

WorkloadContent
fibRecursive Fibonacci number of 30
sieveSieve of Eratosthenes up to 1,000,000
mandelbrotMandelbrot set on a grid of 200 × 200
stringsFormats, splits and hashes 20,000 strings
sortSorts 200,000 integers
mapInserts 500,000 times into a hash map

WebAssembly runtimes​

Compute workloads (ms, lower is better). wasmtime is a JIT compiler and is shown for reference, and wasm3 is an interpreter.

Compute workloads of WaaS, xLua, Lua-CSharp, wasm3 and wasmtime on a logarithmic scale

The time of the guest language differs by workload.

Rust and MoonBit guests on WaaS, wasm3 and wasmtime on a logarithmic scale

WorkloadWaaS CoreCLR (Rust)WaaS CoreCLR (MoonBit)WaaS Burst (Rust)WaaS Burst (MoonBit)wasm3 (Rust)wasm3 (MoonBit)wasmtime (Rust)wasmtime (MoonBit)
fib34.165.419.744.617.926.42.03.8
sieve40.2176.842.0111.019.078.02.35.1
mandelbrot16.536.216.438.17.315.51.72.2
strings40.8518.760.9524.416.3n/a2.216.9
sort64.2357.156.4309.123.9144.54.619.8
map81.2613.779.4417.036.7232.34.420.0

n/a: wasm3 cannot compile a function of this module.

Lua​

Compute workloads (ms, lower is better).

WorkloadxLuaLua-CSharp
fib49.3393.3
sieve33.4230.4
mandelbrot32.6182.0
strings24.8181.3
sort50.2154.5
map8.870.1

Calls between scripts and the host​

The values are in ns/call, lower is better. ffi_* are calls from scripts to the host, and callback_* are calls from the host to scripts.

Calls between scripts and the host on a logarithmic scale

WorkloadWaaS CoreCLR (Rust)WaaS CoreCLR (MoonBit)WaaS Burst (Rust)WaaS Burst (MoonBit)xLuaLua-CSharp
ffi_nop4046587027207
ffi_add5144698340413
ffi_string_in762514041,008114304
ffi_string_out3072,3498053,13967259
ffi_struct1151,6463741,69494362
ffi_resource1548533334731318
callback_add1289546946630373
callback_string3371,4369672,83768361
  • MoonBit uses UTF-16 strings, which are not covered by the direct path of the bindings for UTF-8, and its bindings allocate memory on the guest side.
  • Strings and records take longer than numbers, because they are stored in the memory of the guest.

Load​

The values are in ms, lower is better.

EngineEngine initLoadInstantiateFirst callTotal
WaaS, first in a process0.00012.0032.6270.89315.524
WaaS (MoonBit), first in a process0.00011.7972.7140.09714.608
xLua, first in a process6.5510.0980.7990.0847.533
xLua (bytecode), first in a process6.5010.0210.8040.0837.409
Lua-CSharp, first in a process0.7821.8330.2460.0122.873
WaaS, repeated0.0006.2490.5880.0346.871
WaaS (MoonBit), repeated0.0005.0180.2670.0055.290
xLua, repeated0.8410.0900.7240.0051.661
xLua (bytecode), repeated0.9400.0180.7130.0031.674
Lua-CSharp, repeated0.0300.3210.0170.0020.370
WaaS CoreCLR (Rust), repeated----9.724
WaaS CoreCLR (MoonBit), repeated----5.070
wasm3 (Rust), repeated----0.014
wasm3 (MoonBit), repeated----0.026
wasmtime (Rust), repeated----23.618
wasmtime (MoonBit), repeated----20.037
  • Loading a component validates and translates all of its functions, and the time grows with the size of the component.
  • wasm3 compiles functions when they are first called, which is not included in its values.
  • The first load on IL2CPP includes the initialization of the runtime.

Reading the results​

  • Numbers are compared on a desktop x64 CPU. Other platforms have not been measured.
  • The results depend on the code that a compiler generates for a guest. For example, map runs the hash function of the guest in the interpreter, while Lua uses a table implemented in C.
  • Performance is one of the factors in choosing an engine. WaaS also offers sandboxing and a choice of guest languages, and does not need JIT compilation.