aduros / wasm4

Build retro games using WebAssembly for a fantasy console.
https://wasm4.org
ISC License
1.17k stars 171 forks source link

Recommend wasm-snip for Rust wasm binaries #238

Open jamesmcm opened 2 years ago

jamesmcm commented 2 years ago

Hi,

One thing I noticed working through the tutorial is the moment you do anything that can panic e.g. FRAMEBUFFER.as_mut().unwrap() the binary size explodes from ~800 bytes to 27 kilobytes as Rust includes all the panicking and formatting code.

You can get a reasonable binary size by using wasm-snip to remove that code:

In Cargo.toml

[profile.release]
opt-level = "z"
lto = true
panic = 'abort'
debug = true
$ cargo build --release
$ wasm-snip --snip-rust-fmt-code --snip-rust-panicking-code target/wasm32-unknown-unknown/cart.wasm
$ w4 run target/wasm32-unknown-unknown/release/cart2.wasm

And it seems to work as expected (note this probably means having to avoid use std::fmt::* stuff though, but it's probably best practice to largely avoid the standard library anyway).

It might be worth adding language speciifc notes to the docs for stuff like this,

ayuusweetfish commented 2 years ago

I'd like to share my current approach for even smaller binary sizes: run the output through wasm-snip, followed by wasm-opt (from binaryen) followed by wasm-strip (from WABT) and another pass through wasm-opt (not sure how, but sometimes it further prunes off a few bytes).

Flags:

(Upd. Found that others are using exactly the same flags, with further experimentation here: christopher-kleine/wasm-4-tutorial-games#17)

Meanwhile, it is worth mentioning that string literals introduced by panicking subroutines still cannot be properly pruned by these tools. (source, archive; the problem persists as of today.)

Also, see the same blog post for suggestions for avoiding panics. Perhaps also consider going for unsafe code in a safe function when it comes to raw memory access.

maxcurzi commented 2 years ago

A note on the very first comment, the bash commands didn't work for me for two reasons 1) The "cart.wasm" was on the release subfolder 2) wasm-snip as is outputted the result in the command line so I needed to redirect it in another cart file

This is the set of commands that worked for me:

cargo build --release
wasm-snip --snip-rust-fmt-code --snip-rust-panicking-code target/wasm32-unknown-unknown/release/cart.wasm > target/wasm32-unknown-unknown/release/cart_snipped.wasm
w4 run target/wasm32-unknown-unknown/release/cart_snipped.wasm

and optionally (for further space reduction)

wasm-opt target/wasm32-unknown-unknown/release/cart_snipped.wasm -Oz --zero-filled-memory --strip-producers --dce --output target/wasm32-unknown-unknown/release/cart_snipped_optimized.wasm
w4 run target/wasm32-unknown-unknown/release/cart_snipped_optimized.wasm