Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The WASM bindings crate

Create a thin layer to define WebAssembly bindings:

cargo new --lib wasm_bindings
cd wasm_bindings

Dependencies

We will add dependencies to:

  • wasm-bindgen for creating WebAssembly bindings, and
  • our own computation crate, and
  • orx-parallel with its wasm feature enabled.

The wasm feature belongs on the direct orx-parallel dependency in the bindings crate; the computation crate stays independent of WebAssembly configuration.

Update par_wasm/wasm_bindings/Cargo.toml as follows:

[package]
name = "wasm_bindings"
version = "0.1.0"
edition = "2024"
publish = false

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
computation = { path = "../computation" }
orx-parallel = { version = "4", features = ["wasm"] }
wasm-bindgen = "0.2"

When the parallel computation involves significant and frequent memory allocation, enable wasm-allocator on this orx-parallel dependency as well, it can prevent WebAssembly memory allocation from becoming a bottleneck that makes multithreaded computation slower than single-threaded computation.

orx-parallel = { version = "4", features = ["wasm", "wasm-allocator"] }

Exposed functions

Update par_wasm/wasm_bindings/src/lib.rs as follows:

#![allow(unused)]
fn main() {
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn calculate_fibonacci(workload: u32, num_threads: u32) -> u64 {
    computation::calculate_fibonacci(workload as usize, num_threads as usize)
}

#[wasm_bindgen]
pub fn mandelbrot_checksum(limit: u32, num_threads: u32) -> u32 {
    computation::mandelbrot_checksum(limit as usize, num_threads as usize) as u32
}
}

If you have a allocation-heavy computation, and hence, enabled wasm-allocator feature, add the following line to par_wasm/wasm_bindings/src/lib.rs as well. This will replace the default WASM allocator with an allocator having 32 shards that is specialized for parallel computations.

#![allow(unused)]
fn main() {
#[cfg(target_arch = "wasm32")]
#[global_allocator]
static GLOBAL_ALLOCATOR: orx_parallel::WasmParallelAllocator<32> =
    orx_parallel::WasmParallelAllocator::new();
}

Notice that we keep this layer as thin as possible:

  • we make necessary type conversions,
  • call our computation crate functions.

Build (optional)

You may try building this crate before implementing the frontend:

RUSTUP_TOOLCHAIN=nightly \
CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS='-C target-feature=+atomics -C link-arg=--shared-memory -C link-arg=--max-memory=1073741824 -C link-arg=--import-memory -C link-arg=--export=__heap_base -C link-arg=--export=__wasm_init_tls -C link-arg=--export=__tls_size -C link-arg=--export=__tls_align -C link-arg=--export=__tls_base' \
cargo build \
  --target wasm32-unknown-unknown \
  --release \
  -Z build-std=panic_abort,std

These flags enable multi-threaded WebAssembly execution: +atomics and --shared-memory enable atomic operations and shared linear memory for thread coordination, --max-memory sets the memory limit, --import-memory allows the host to provide memory, and the __* exports expose thread-local storage (TLS) setup functions needed for proper thread initialization.

As we will see in the next section, this build step will be automated using orx-parallel-wasm.

One level up into par_wasm directory:

cd ..