Precompiled modules

View Source

serialize/1 turns a compiled module into Wasmtime's precompiled form, and deserialize/1 loads that form without compiling. You need it when compile time matters at start-up (the 30 MB CPython module takes about 350 ms to compile and 4 ms to deserialize from its 17 MB precompiled form) or when the machine that runs modules should not carry the compiler at all.

Compile once, at build time

{ok, Bin} = file:read_file("python.wasm"),
{ok, Mod} = wasmtime:compile(Bin),
{ok, Pre} = wasmtime:serialize(Mod),
ok = file:write_file("python.cwasm", Pre).

Load at run time

{ok, Pre} = file:read_file("python.cwasm"),
{ok, Mod} = wasmtime:deserialize(Pre),
{ok, Inst} = wasmtime:instantiate(Mod, ...).

The module behaves exactly like one from compile/1: same exports, same options, same instances.

Keep a cache

load(WasmPath) ->
    Cache = WasmPath ++ ".cwasm",
    case file:read_file(Cache) of
        {ok, Pre} ->
            case wasmtime:deserialize(Pre) of
                {ok, Mod} -> {ok, Mod};
                {error, _} -> compile_and_cache(WasmPath, Cache)
            end;
        _ ->
            compile_and_cache(WasmPath, Cache)
    end.

compile_and_cache(WasmPath, Cache) ->
    {ok, Bin} = file:read_file(WasmPath),
    {ok, Mod} = wasmtime:compile(Bin),
    {ok, Pre} = wasmtime:serialize(Mod),
    ok = file:write_file(Cache, Pre),
    {ok, Mod}.

A stale cache (other Wasmtime version, other CPU features) fails deserialize/1 with class => compile and falls through to a fresh compile.

Compile options and compatibility

A precompiled module records how it was compiled. At load time Wasmtime compares that with the engine:

OptionChecked at loadSo
fuelyes, exactlyload with deserialize(Bin, #{fuel => true}) (deserialize/1 tries it too)
opt_levelnoloads on any engine, including runtime-only builds; the code keeps the level it was compiled at
proposalsas a subseta module compiled with proposals disabled loads on the defaults

module_options/1 on the compile side tells you what to pass on the load side.

Notes

  • The precompiled form is tied to the Wasmtime version in scripts/wasmtime.version and to the CPU features of the machine that produced it. Wasmtime checks both and refuses a mismatch.
  • It contains machine code. Wasmtime verifies the header, not the code, so deserialize/1 must only ever see bytes that came from serialize/1 on a machine you trust. Never deserialize input from a user; give users .wasm files and compile/1, which validates everything.
  • The bytes are not a WebAssembly module: compile/1 rejects them, and deserialize/1 rejects a .wasm file.
  • A node that only ever loads precompiled modules can be built without the compiler: WASMTIME_RUNTIME_ONLY=1 rebar3 compile, 4 MB instead of 25. See building, "Runtime-only builds".