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-bindgenfor creating WebAssembly bindings, and- our own
computationcrate, and orx-parallelwith itswasmfeature 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:
+atomicsand--shared-memoryenable atomic operations and shared linear memory for thread coordination,--max-memorysets the memory limit,--import-memoryallows 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 ..