README.md
1<div align="center">
2 <h1>Wizer</h1>
3
4 <p>
5 <strong>The WebAssembly Pre-Initializer!</strong>
6 </p>
7
8 <strong>A <a href="https://bytecodealliance.org/">Bytecode Alliance</a> project</strong>
9
10 <p>
11 <a href="https://bytecodealliance.zulipchat.com/#narrow/stream/223391-wasm"><img src="https://img.shields.io/badge/zulip-join_chat-brightgreen.svg" alt="zulip chat" /></a>
12 <a href="https://docs.rs/wasmtime-wizer"><img src="https://docs.rs/wasmtime-wizer/badge.svg" alt="Documentation Status" /></a>
13 </p>
14
15 <h3>
16 <a href="https://docs.rs/wasmtime-wizer">API Docs</a>
17 <span> | </span>
18 <a href="https://github.com/bytecodealliance/wasmtime/blob/main/CONTRIBUTING.md">Contributing</a>
19 <span> | </span>
20 <a href="https://bytecodealliance.zulipchat.com/#narrow/stream/223391-wasm">Chat</a>
21 </h3>
22</div>
23
24* [About](#about)
25* [Install](#install)
26* [Example Usage](#example-usage)
27* [Caveats](#caveats)
28* [Using Wizer as a Library](#using-wizer-as-a-library)
29* [How Does it Work?](#how-does-it-work)
30
31## About
32
33Don't wait for your Wasm module to initialize itself, pre-initialize it! Wizer
34instantiates your WebAssembly module, executes its initialization function, and
35then snapshots the initialized state out into a new WebAssembly module. Now you
36can use this new, pre-initialized WebAssembly module to hit the ground running,
37without making your users wait for that first-time set up code to complete.
38
39The improvements to start up latency you can expect will depend on how much
40initialization work your WebAssembly module needs to do before it's ready. Some
41initial benchmarking shows between 1.35 to 6.00 times faster instantiation and
42initialization with Wizer, depending on the workload:
43
44| Program | Without Wizer | With Wizer | Speedup |
45|------------------------|--------------:|-----------:|-----------------:|
46| [`regex`][regex-bench] | 248.85 us | 183.99 us | **1.35x faster** |
47| [UAP][uap-bench] | 98.297 ms | 16.385 ms | **6.00x faster** |
48
49[regex-bench]: https://github.com/bytecodealliance/wasmtime/tree/main/crates/wizer/benches/regex-bench
50[uap-bench]: https://github.com/bytecodealliance/wasmtime/tree/main/crates/wizer/benches/uap-bench
51
52Not every program will see an improvement to instantiation and start up
53latency. For example, Wizer will often increase the size of the Wasm module's
54`Data` section, which could negatively impact network transfer times on the
55Web. However, the best way to find out if your Wasm module will see an
56improvement is to try it out! Adding an initialization function isn't too hard.
57
58Finally, you can likely see further improvements by running
59[`wasm-opt`][binaryen] on the pre-initialized module. Beyond the usual benefits
60that `wasm-opt` brings, the module likely has a bunch of initialization-only
61code that is no longer needed now that the module is already initialized, and
62which `wasm-opt` can remove.
63
64[binaryen]: https://github.com/WebAssembly/binaryen
65
66## Example Usage
67
68First, make sure your Wasm module exports an initialization function named
69`wizer-initialize`. For example, in Rust you can export it like this:
70
71```rust
72#[unsafe(export_name = "wizer-initialize")]
73pub extern "C" fn init() {
74 // Your initialization code goes here...
75}
76```
77
78For a complete C++ example, see [this](https://github.com/bytecodealliance/wizer/tree/main/examples/cpp).
79
80Then, if your Wasm module is named `input.wasm`, run the `wizer` CLI:
81
82```shell-session
83wasmtime wizer input.wasm -o initialized.wasm
84```
85
86Now you have a pre-initialized version of your Wasm module at
87`initialized.wasm`!
88
89More details, flags, and options can be found via `--help`:
90
91```shell-session
92wasmtime wizer --help
93```
94
95## Caveats
96
97* The initialization function may not call any imported functions. Doing so will
98 trigger a trap and `wizer` will exit. You can, however, allow WASI calls via
99 the `--allow-wasi` flag.
100
101* The Wasm module may not import globals, tables, or memories.
102
103* Reference types are not supported yet. It isn't 100% clear yet what the best
104 approach to snapshotting `externref` tables is.
105
106## Using Wizer as a Library
107
108Add a dependency in your `Cargo.toml`:
109
110```shell-session
111cargo add wasmtime-wizer
112```
113
114And then use the `wizer::Wizer` builder to configure and run Wizer:
115
116```rust
117use wizer::Wizer;
118
119let input_wasm = get_input_wasm_bytes();
120let initialized_wasm_bytes = Wizer::new()
121 .run(&mut store, &input_wasm, |store, module| {
122 linker.instantiate(store, module)
123 })?;
124```
125
126## How Does it Work?
127
128First we instantiate the input Wasm module with Wasmtime and run the
129initialization function. Then we record the Wasm instance's state:
130
131* What are the values of its globals?
132* What regions of memory are non-zero?
133
134Then we rewrite the Wasm binary by initializing its globals directly to their
135recorded state, and removing the module's old data segments and replacing them
136with data segments for each of the non-zero regions of memory we recorded.
137
138Want some more details? Check out the talk ["Hit the Ground Running: Wasm
139Snapshots for Fast Start
140Up"](https://fitzgeraldnick.com/2021/05/10/wasm-summit-2021.html) from the 2021
141WebAssembly Summit.
142