Tuning Wasmtime for Fast Wasm Execution

To tune Wasmtime for faster Wasm execution, consider the following tips.

Enable Cranelift

Cranelift is an optimizing compiler. Compared to alternative strategies like the Winch "baseline" compiler, it translates Wasm into faster machine code, but compilation is slower. Cranelift is similar to the optimizing tier of browsers' just-in-time Wasm engines, such as SpiderMonkey's Ion tier or V8's TurboFan tier.

See the API docs for wasmtime::Strategy and wasmtime::Config::strategy for more details.

Configure Wasmtime to Elide Explicit Bounds Checks

Wasm programs are sandboxed and may only access their linear memories. Attempts to access beyond the bounds of a linear memory results in a trap, and this prevents the Wasm guest from stealing data from host memory, or from another concurrently running Wasm instance. Explicitly bounds-checking every linear memory operation performed by a Wasm guest is expensive: it has been measured to create between a 1.2x to 1.8x slow down, depending on a number of factors. Luckily, Wasmtime can usually omit explicit bounds checks by relying on virtual memory guard pages. This requires enabling signals-based traps (on by default for non-bare-metal builds), running Wasm on a 64-bit host architecture, and ensuring that memory reservations and guard pages are appropriately sized (again, configured by default for 64-bit architectures).

To elide any explicit bounds checks, Wasm linear memories must have at least a 4GiB (1 << 32 bytes) reservation. If a memory instruction has an additional static offset immediate, then the bounds check can only be elided when there is a memory guard of at least that offset's size. Using a 4GiB guard region allows Wasmtime to elide explicit bounds checks regardless of the static memory offset immediate. While small static offset immediate values are common, very large values are exceedingly rare, so you can get almost all of the benefits while consuming less virtual memory address space by using, for example, 32MiB guards.

See the API docs for wasmtime::Config::signals_based_traps, wasmtime::Config::memory_reservation, and wasmtime::Config::memory_reservation for more details.

Force-Enable ISA Extensions

This section can be ignored if you are compiling and running Wasm programs on the same machine. In this scenario, Wasmtime will automatically detect which ISA extensions (such as AVX on x86_64) are available, and you do not need to configure anything yourself.

However, if you are compiling a Wasm program on one machine and then running that pre-compiled Wasm program on another machine, then during compilation Wasmtime cannot automatically detect which ISA extensions will be available on the machine on which you actually execute the pre-compiled Wasm program. Configuring which ISA extensions are available on the target architecture that will run the pre-compiled Wasm programs can have a large impact for certain Wasm programs, particularly those using SIMD instructions.

See the API docs for wasmtime::Config::detect_host_feature for more details.

Putting It All Together

//! Tuning Wasmtime for fast Wasm execution.

use wasmtime::{Config, Engine, Result, Strategy};

fn main() -> Result<()> {
    let mut config = Config::new();

    // Enable the Cranelift optimizing compiler.
    config.strategy(Strategy::Cranelift);

    // Enable signals-based traps. This is required to elide explicit
    // bounds-checking.
    config.signals_based_traps(true);

    // Configure linear memories such that explicit bounds-checking can be
    // elided.
    config.memory_reservation(1 << 32);
    config.memory_guard_size(1 << 32);

    if CROSS_COMPILING {
        // When cross-compiling, force-enable various ISA extensions.
        //
        // Warning: it is wildly unsafe to run a pre-compiled Wasm program that
        // enabled a particular ISA extension on a machine that does not
        // actually support that ISA extension!!!
        unsafe {
            if let Err(error) = config.target("x86_64") {
                eprintln!(
                    "Wasmtime was not compiled with the x86_64 backend for \
                     Cranelift enabled: {error:?}",
                );
                return Ok(());
            }
            config.detect_host_feature(|feature| {
                match feature {
                    // A few example x86_64 features you can configure.
                    "sse4.1" => Some(true),
                    "avx" => Some(true),
                    "avx2" => Some(true),
                    "lzcnt" => Some(true),
                    _ => None,
                }
            });
        }
    }

    // Build an `Engine` with our `Config` that is tuned for fast Wasm
    // execution.
    let engine = match Engine::new(&config) {
        Ok(engine) => engine,
        Err(error) => {
            eprintln!("Configuration is incompatible with this host platform: {error:?}");
            return Ok(());
        }
    };

    // Now we can use `engine` to compile and/or run some Wasm programs...

    let _ = engine;
    Ok(())
}

const CROSS_COMPILING: bool = false;

See Also