Having developed an implementation of the SHA256 algorithm directly in WebAssembly Text, I was pleased to get the compiled binary down to about 2.5Kb.
By way of comparison, I decided to implement the equivalent functionality in Rust, compile it to WebAssembly and then look at the size of the WASM binary.
As a result, I now have two versions of the Rust implementation that implement file I/O using different libraries:
- The standard Rust library
- The WASI library
The standard library version can be run using cargo run
as you would with any other Rust program, but to run either version from WebAssembly, they must be compiled for the wasm32-wasip1
target, then run using a WebAssembly host environment such as wasmer
or wasmtime
.
Using the standard Rust library to perform all the file I/O is straight forward and does not require very much coding. The optimized WebAssembly binary is about 72Kb.
Using the WASI library instead to perform all the file I/O requires that you write a specific Rust wrapper function for each WASI function you wish to call. This requires much more effort, but the optimized WebAssembly binary is 42Kb.
Look at the coding in this repo https://github.com/ChrisWhealy/wasm_sha256
The optimized WebAssembly binary is 2.5Kb.
😎
Before this program can be run from WebAssembly, you must first install the wasm32-wasip1
compilation target.
$ rustup target add wasm32-wasip1
info: downloading component 'rust-std' for 'wasm32-wasip1'
info: installing component 'rust-std' for 'wasm32-wasip1'
21.3 MiB / 21.3 MiB (100 %) 14.7 MiB/s in 1s
Simply use cargo run
to build and run the std
version of the binary:
$ cargo run --bin std --release ./src/bin/std.rs
Finished `release` profile [optimized] target(s) in 0.04s
Running `target/release/std ./src/bin/std.rs`
008d580e17bb8da5bf3458037ab9e39b2a48ee2688bf004abf4529bf1c35ea1c ./src/bin/std.rs
$ ./build.sh std
Build std -> ./target/wasm32-wasip1/release/std.opt.wasm
Compiling wit-bindgen-rt v0.39.0
Compiling bitflags v2.9.2
Compiling wee_alloc v0.4.5
Compiling cfg-if v0.1.10
Compiling memory_units v0.4.0
Compiling wasi v0.14.2+wasi-0.2.4
Compiling sha256 v1.0.0 (/Users/chris/Developer/rust/sha256)
Finished `release` profile [optimized] target(s) in 3.60s
The optimized WebAssembly module ./target/wasm32-wasip1/release/std.opt.wasm
is about 71Kb.
$ wasmer run ./target/wasm32-wasip1/release/std.opt.wasm --mapdir /::./src/bin -- std.rs
008d580e17bb8da5bf3458037ab9e39b2a48ee2688bf004abf4529bf1c35ea1c std.rs
$ ./build.sh wasi
Build wasi -> ./target/wasm32-wasip1/release/wasi.opt.wasm
Compiling sha256 v1.0.0 (/Users/chris/Developer/rust/sha256)
Finished `release` profile [optimized] target(s) in 1.53s
The optimized WebAssembly module ./target/wasm32-wasip1/release/wasi.opt.wasm
is about 42Kb.
$ wasmer run ./target/wasm32-wasip1/release/std.opt.wasm --mapdir /::./src/bin -- std.rs
008d580e17bb8da5bf3458037ab9e39b2a48ee2688bf004abf4529bf1c35ea1c std.rs
In spite of the fact that this binary is about 1/3 smaller than the std
version, there is still some unavoidable Rust/WASI baggage.
The use of standard Rust functionality is convenient, but the cost of such convenience is the fact that every time you do "normal" things like allocate a String
or use the format!
macro, you implicitly bring into scope a large amount of extra coding that can (with some effort) be removed.