Convert Figma logo to code with AI

bytecodealliance logowizer

The WebAssembly Pre-Initializer

1,022
60
1,022
21

Top Related Projects

Facilitating high-level interactions between Wasm modules and JavaScript

19,923

🚀 Fast, secure, lightweight containers based on WebAssembly

16,700

A lightweight WebAssembly runtime that is fast, secure, and standards-compliant

Optimizer and compiler/toolchain library for WebAssembly

Emscripten: An LLVM-to-WebAssembly Compiler

Monorepo for Javascript WebAssembly packages by Wasmer

Quick Overview

Wizer is a WebAssembly pre-initialization tool that allows you to run a WebAssembly module, capture its state, and create a new WebAssembly module with that state built-in. This process can significantly reduce startup time for WebAssembly applications by performing expensive computations ahead of time.

Pros

  • Significantly reduces startup time for WebAssembly applications
  • Works with existing WebAssembly modules without requiring source code changes
  • Supports a wide range of WebAssembly features and use cases
  • Can be integrated into existing build pipelines

Cons

  • May increase the size of the resulting WebAssembly module
  • Not all types of initialization can be pre-computed (e.g., those depending on runtime input)
  • Requires an additional build step in the development process
  • May not be beneficial for all types of applications, especially those with minimal initialization

Code Examples

  1. Basic usage of Wizer:
use wizer::Wizer;

let wasm_bytes = std::fs::read("input.wasm")?;
let wizer = Wizer::new();
let initialized_wasm = wizer.run(&wasm_bytes)?;
std::fs::write("output.wasm", initialized_wasm)?;
  1. Configuring Wizer with custom options:
use wizer::{Wizer, WizerBuilder};

let wizer = WizerBuilder::new()
    .allow_wasi(true)
    .run_parallel(true)
    .inherit_stdio(true)
    .build();

let initialized_wasm = wizer.run(&wasm_bytes)?;
  1. Using Wizer with a custom initialization function:
use wizer::Wizer;

let wizer = Wizer::new();
let initialized_wasm = wizer
    .run_with_init_func(&wasm_bytes, "custom_init_function")?;

Getting Started

To use Wizer in your Rust project, add it to your Cargo.toml:

[dependencies]
wizer = "1.0"

Then, in your Rust code:

use wizer::Wizer;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let wasm_bytes = std::fs::read("input.wasm")?;
    let wizer = Wizer::new();
    let initialized_wasm = wizer.run(&wasm_bytes)?;
    std::fs::write("output.wasm", initialized_wasm)?;
    Ok(())
}

This example reads a WebAssembly module, initializes it using Wizer, and writes the resulting module to a new file.

Competitor Comparisons

Facilitating high-level interactions between Wasm modules and JavaScript

Pros of wasm-bindgen

  • Provides a more comprehensive solution for JavaScript interoperability
  • Offers automatic generation of TypeScript definitions
  • Supports a wider range of Rust types and features

Cons of wasm-bindgen

  • Requires additional build steps and dependencies
  • Can increase the size of the generated Wasm module
  • May introduce some runtime overhead for type conversions

Code Comparison

wasm-bindgen:

#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

wizer:

#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
    a + b
}

wasm-bindgen focuses on providing a seamless integration between Rust and JavaScript, offering rich type conversions and automatic bindings generation. It's particularly useful for complex projects with extensive JavaScript interop needs.

wizer, on the other hand, is a WebAssembly pre-initialization tool that aims to reduce startup times by running the initialization phase of a Wasm module ahead of time. It doesn't provide the same level of JavaScript integration as wasm-bindgen but can be beneficial for optimizing Wasm module performance.

While wasm-bindgen requires additional setup and may increase module size, it offers more comprehensive JavaScript integration. wizer is simpler to use and can improve startup performance but lacks the advanced interop features of wasm-bindgen.

The choice between the two depends on the specific needs of your project, balancing between rich JavaScript integration and optimized Wasm performance.

19,923

🚀 Fast, secure, lightweight containers based on WebAssembly

Pros of Wasmer

  • More comprehensive WebAssembly runtime with broader language support
  • Offers both standalone runtime and embeddable library options
  • Active development with frequent updates and larger community

Cons of Wasmer

  • Larger footprint and potentially higher resource usage
  • More complex setup and configuration for specific use cases
  • May introduce runtime overhead compared to ahead-of-time compilation

Code Comparison

Wizer (initialization):

let module = Module::from_file(env, "input.wasm")?;
let config = Config::new();
let instance = wizer::run(&module, &config)?;

Wasmer (runtime execution):

let module = Module::from_file(&store, "input.wasm")?;
let import_object = imports! {};
let instance = Instance::new(&module, &import_object)?;

While Wizer focuses on ahead-of-time initialization to improve startup times, Wasmer provides a full runtime environment for executing WebAssembly modules. Wizer's approach can lead to faster initial execution, but Wasmer offers more flexibility and broader runtime capabilities. The choice between them depends on specific project requirements, such as startup performance vs. runtime features and language support.

16,700

A lightweight WebAssembly runtime that is fast, secure, and standards-compliant

Pros of Wasmtime

  • More comprehensive WebAssembly runtime with broader feature set
  • Supports multiple languages and integrations (e.g., Rust, C, Python)
  • Active development with frequent updates and improvements

Cons of Wasmtime

  • Larger codebase and potentially more complex setup
  • May have higher runtime overhead for certain use cases

Code Comparison

Wasmtime (Rust API usage):

let engine = Engine::default();
let module = Module::from_file(&engine, "example.wasm")?;
let instance = Instance::new(&mut store, &module, &[])?;

Wizer (Command-line usage):

wizer input.wasm -o output.wasm

Summary

Wasmtime is a full-featured WebAssembly runtime, while Wizer is a specialized tool for WebAssembly module initialization. Wasmtime offers broader functionality and language support, making it suitable for various WebAssembly applications. Wizer, on the other hand, focuses on optimizing WebAssembly modules by pre-initializing them, potentially reducing startup times.

Wasmtime's versatility comes with a more complex codebase and potential runtime overhead. Wizer's specialized approach may offer performance benefits in specific scenarios, particularly for reducing cold start times in serverless environments.

The choice between the two depends on the specific requirements of your project. Wasmtime is ideal for general WebAssembly runtime needs, while Wizer can be a valuable tool for optimizing WebAssembly module initialization in performance-critical applications.

Optimizer and compiler/toolchain library for WebAssembly

Pros of Binaryen

  • More comprehensive toolset for WebAssembly optimization and compilation
  • Longer development history and wider adoption in the WebAssembly ecosystem
  • Supports a broader range of WebAssembly features and optimizations

Cons of Binaryen

  • Larger and more complex codebase, potentially harder to contribute to or customize
  • May have higher resource usage for simple WebAssembly transformations
  • Less focused on specific use cases like ahead-of-time instantiation

Code Comparison

Binaryen (C++):

Module module;
Expression* expr = module.builder().makeConst(Literal(42));
Function* func = module.addFunction("answer", Signature(), {}, expr);

Wizer (Rust):

let mut module = Module::new();
let function = Function::new(vec![], vec![]);
module.add_function("answer", function);

Summary

Binaryen is a more comprehensive WebAssembly toolkit with a wider range of features and optimizations. It's well-established in the ecosystem but may be more complex for simple tasks. Wizer, on the other hand, is more focused on ahead-of-time instantiation and may be simpler to use for specific use cases. The code comparison shows that Binaryen uses C++ and provides more low-level control, while Wizer uses Rust and offers a higher-level API for module manipulation.

Emscripten: An LLVM-to-WebAssembly Compiler

Pros of Emscripten

  • Mature and widely adopted toolchain for compiling C/C++ to WebAssembly
  • Extensive ecosystem and community support
  • Provides a complete runtime environment, including libc implementation

Cons of Emscripten

  • Larger output size due to included runtime and libraries
  • Slower startup time for applications
  • More complex setup and configuration process

Code Comparison

Emscripten example:

#include <emscripten.h>
#include <stdio.h>

int main() {
    printf("Hello, Emscripten!\n");
    return 0;
}

Wizer example (assuming pre-compiled Wasm module):

use wizer::Wizer;

let wasm = std::fs::read("input.wasm")?;
let wized = Wizer::new()
    .run(&wasm)?;
std::fs::write("output.wasm", &wized)?;

Summary

Emscripten is a comprehensive toolchain for compiling C/C++ to WebAssembly, offering a full runtime environment and extensive ecosystem support. However, it can result in larger output sizes and slower startup times.

Wizer, on the other hand, is a WebAssembly pre-initialization tool that focuses on reducing startup time and memory usage by running the initialization phase ahead of time. It works with existing Wasm modules and doesn't provide a complete compilation toolchain like Emscripten.

The choice between the two depends on specific project requirements, with Emscripten being more suitable for complex C/C++ projects requiring full runtime support, while Wizer is beneficial for optimizing existing Wasm modules for faster startup.

Monorepo for Javascript WebAssembly packages by Wasmer

Pros of wasmer-js

  • Provides a complete WebAssembly runtime environment for JavaScript
  • Offers a high-level API for easy integration of WebAssembly modules
  • Supports both browser and Node.js environments

Cons of wasmer-js

  • May have higher overhead due to its comprehensive feature set
  • Potentially larger bundle size, which could impact load times in web applications

Code Comparison

wasmer-js:

import { WASI } from '@wasmer/wasi';
import { WasmFs } from '@wasmer/wasmfs';

const wasmFs = new WasmFs();
const wasi = new WASI({
  args: [],
  env: {},
  bindings: {
    ...WASI.defaultBindings,
    fs: wasmFs.fs
  }
});

const instance = await WebAssembly.instantiate(wasmBytes, {
  wasi_snapshot_preview1: wasi.wasiImport
});

wasi.start(instance);

wizer:

use wizer::Wizer;

let wasm = std::fs::read("input.wasm")?;
let config = Wizer::new()
    .allow_wasi(true)
    .run_initializers(true);
let initialized = config.run(&wasm)?;
std::fs::write("output.wasm", initialized)?;

The code examples demonstrate the different approaches: wasmer-js provides a runtime environment for executing WebAssembly, while wizer focuses on ahead-of-time initialization of WebAssembly modules.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Wizer

The WebAssembly Pre-Initializer!

A Bytecode Alliance project

build status zulip chat Documentation Status

API Docs | Contributing | Chat

About

Don't wait for your Wasm module to initialize itself, pre-initialize it! Wizer instantiates your WebAssembly module, executes its initialization function, and then snapshots the initialized state out into a new WebAssembly module. Now you can use this new, pre-initialized WebAssembly module to hit the ground running, without making your users wait for that first-time set up code to complete.

The improvements to start up latency you can expect will depend on how much initialization work your WebAssembly module needs to do before it's ready. Some initial benchmarking shows between 1.35 to 6.00 times faster instantiation and initialization with Wizer, depending on the workload:

ProgramWithout WizerWith WizerSpeedup
regex248.85 us183.99 us1.35x faster
UAP98.297 ms16.385 ms6.00x faster

Not every program will see an improvement to instantiation and start up latency. For example, Wizer will often increase the size of the Wasm module's Data section, which could negatively impact network transfer times on the Web. However, the best way to find out if your Wasm module will see an improvement is to try it out! Adding an initialization function isn't too hard.

Finally, you can likely see further improvements by running wasm-opt on the pre-initialized module. Beyond the usual benefits that wasm-opt brings, the module likely has a bunch of initialization-only code that is no longer needed now that the module is already initialized, and which wasm-opt can remove.

Install

Download the a pre-built release from the releases page. Unarchive the binary and place it in your $PATH.

Alternatively you can install via cargo:

cargo install wizer --all-features

Example Usage

First, make sure your Wasm module exports an initialization function named wizer.initialize. For example, in Rust you can export it like this:

#[export_name = "wizer.initialize"]
pub extern "C" fn init() {
    // Your initialization code goes here...
}

For a complete C++ example, see this.

Then, if your Wasm module is named input.wasm, run the wizer CLI:

wizer input.wasm -o initialized.wasm

Now you have a pre-initialized version of your Wasm module at initialized.wasm!

More details, flags, and options can be found via --help:

wizer --help

Caveats

  • The initialization function may not call any imported functions. Doing so will trigger a trap and wizer will exit. You can, however, allow WASI calls via the --allow-wasi flag.

  • The Wasm module may not import globals, tables, or memories.

  • Reference types are not supported yet. It isn't 100% clear yet what the best approach to snapshotting externref tables is.

Using Wizer as a Library

Add a dependency in your Cargo.toml:

# Cargo.toml

[dependencies]
wizer = "9"

And then use the wizer::Wizer builder to configure and run Wizer:

use wizer::Wizer;

let input_wasm = get_input_wasm_bytes();

let initialized_wasm_bytes = Wizer::new()
    .allow_wasi(true)?
    .run(&input_wasm)?;

Using Wizer with a custom Linker

If you want your module to be able to import other modules during instantiation, you can use the .make_linker(...) builder method to provide your own Linker, for example:

use wizer::Wizer;

let input_wasm = get_input_wasm_bytes();
let initialized_wasm_bytes = Wizer::new()
    .make_linker(Some(Rc::new(|e: &wasmtime::Engine| {
        let mut linker = wasmtime::Linker::new(e);
        linker.func_wrap("foo", "bar", |x: i32| x + 1)?;
        Ok(linker)
    })))
    .run(&input_wasm)?;

Note that allow_wasi(true) and a custom linker are currently mutually exclusive

How Does it Work?

First we instantiate the input Wasm module with Wasmtime and run the initialization function. Then we record the Wasm instance's state:

  • What are the values of its globals?
  • What regions of memory are non-zero?

Then we rewrite the Wasm binary by intializing its globals directly to their recorded state, and removing the module's old data segments and replacing them with data segments for each of the non-zero regions of memory we recorded.

Want some more details? Check out the talk "Hit the Ground Running: Wasm Snapshots for Fast Start Up" from the 2021 WebAssembly Summit.