Currently wasm binary for even this simple function
#![feature(proc_macro)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn run(x: i32, y: i32) -> i32 {
x + y
}
is 230kb, after applying wasm-gc.
Thanks for the report! Are you also compiling with LTO and optimizations?
Actually no, I thought --release had LTO enabled by default. And LTO brought it down to 10 kb. Should I close this issue?
Ok, great!
Note that a bunch of that 10kb comes from the static descriptors that wasm-bindgen emits. Those will eventually go away but if you run the wasm-bindgen CLI and then run wasm-opt from the binaryen toolkit over the output wasm file it should be much smaller.
Eventually though none of that should be necessary and a normal LTO build should be quite small! (after running wasm-bindgen)
@alexcrichton would you consider extending the "Basic Usage" example to run wasm-gc, wasm-opt, and/or whatever wasm-bindgen flags (LTO? --release?) are required to achieve the minimum file size?
Here are steps that worked for me:
Build with --release
cargo build --release --target wasm32-unknown-unknown
Run wasm-bindgen
wasm-bindgen target/wasm32-unknown-unknown/release/js_hello_world.wasm --out-dir .
Run wasm-gc
wasm-gc js_hello_world_bg.wasm js_hello_world_bg_gc.wasm
Run wasm-opt
wasm-opt js_hello_world_bg_gc.wasm -Os -o js_hello_world_bg_gc_opt.wasm
Then, update js_hello_world.js to import js_hello_world_bg_gc_opt.
$ wc -c <js_hello_world_bg.wasm
237298
$ wc -c <js_hello_world_bg_gc.wasm
227057
$ wc -c <js_hello_world_bg_gc_opt.wasm
167750
You could reduce the file size much more. Add the following to Cargo.toml and do cargo build --release.
[profile.release]
lto = true
@markandrus thanks for the report!
This is definitely a general problem of "we need documentation about generating the smallest binaries". I've opened an issue at https://github.com/rust-lang-nursery/rust-wasm/issues/109 for documenting this in the working group's book
Thanks for the tips, y'all!
With lto = true (and the same sequence of transformations), I get
$ wc -c <js_hello_world_gc_opt.wasm
43513
If I add opt-size = 'z', it gets even smaller:
$ wc -c <js_hello_world_gc_opt.wasm
36128
Using wee_alloc and std,
$ wc -c <js_hello_world_gc_opt.wasm
32294
I dont think no_std is an option鈥攁t least for the "Basic Usage" example, since it uses format鈥攕o I'm not sure it can go smaller?
@markandrus the "hello world" example uses string formatting and allocation which are two major contributors to code size. Projects focused on code size tend to avoid at least string formatting and often allocations, so the "hello world" isn't necessarily representative of what a hello-world would look like for a size-concerned application
@alexcrichton I tried #![no_std] but with no luck. It seems wasm_bindgen includes std automatically:
#![no_std] // it seems the compiler just ignores the annotation
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
#![feature(global_allocator)]
#![feature(alloc, core_intrinsics, lang_items)]
extern crate wasm_bindgen;
extern crate alloc;
extern crate wee_alloc;
use wasm_bindgen::prelude::*;
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
/* I have to comment the following lines out to compile the code */
// #[lang = "panic_fmt"]
// extern "C" fn panic_fmt(_args: ::core::fmt::Arguments, _file: &'static str, _line: u32) -> ! {
// use core::intrinsics;
// unsafe {
// intrinsics::abort();
// }
// }
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet(name: &str) {
alert("hello");
}
@michael8090 I think you're likely running up against "string allocation and panicking is large in rust right now", which while hopefully reduced on the next successful nightly isn't necessarily directly related to wasm-bindgen.
I'm trying to compile wasm-bindgen with no_std too, and I got this error:
error[E0433]: failed to resolve. Did you mean `nom::lib::std`?
--> src/wasm.rs:6:1
|
6 | #[wasm_bindgen(module = "./parser_definitions")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Did you mean `nom::lib::std`?
rustc tries to find another libstd somewhere in another crate (here, nom).
Is it possible to compile wasm-bindgen with no_std?
Ah currently the macro can only be used in crates that have std at the root, but I'd love to fix that! Nothing inherently requires std here (but passing things like String does). @Hywan want to open a dedicated issue for that?
Sure, done :-).
I'm having trouble getting a minimal-size setup. My intent was to try to make a small demo module that adds two numbers and build up from there but it's not easy for me to understand the combination of incantations for lto, opt-size, wasm-opt, no_std (I think), no allocator, no formatting (wasm-snip?) that I need. Would it be possible to get an example added to the repo showing the smallest known possible wasm-bindgen output that can be used as a starting point for size-conscious libraries?
@sophiebits sure yeah! I just added a new example which you can also play around with online.
The key things for "simple code" (aka adding numbers) is to enable LTO and to compile with optimizations. That's set by wasm-bindgen's workspace currently.
For a bit larger of an example I've seen the dom example is also quite small in the compile wasm size, which you can also explore online as well.
If you're wondering about a particular piece of code though and how to get it smaller just let me know! I'd love to help out and see what we can improve in the libraries/tooling along the way.
Just tried and the add example works great. When building dom with --release, I get 234,042 bytes and after wasm-opt -Os 170,992 bytes. Just wanted to check if that's the size you'd expect?
Hm odd! When I compile the dom example with --release I get a 1.5k wasm file coming out of wasm-bindgen and 632 bytes after wasm-opt.
@sophiebits are you sure you're compiling with LTO though? When I turn off LTO and opt-level = 's' the output of wasm-bindgen jumps to 217k and the wasm-opt optimized size is 161k. If not though, have some code I could poke around and try to reproduce with?
Ahh. I was testing in the repo directly (having edited your build.sh) but I neglected to update "debug" to "release" in the path ../../target/wasm32-unknown-unknown/release/dom.wasm. I get what you get now!
Heh I've done that more than once before as well :)
Great to see the dramatic reduction of the add example to 600 bytes! What is still in the module? In comparison, IIRC the add example in AssemblyScript is only 80 bytes, so maybe there is still room for improvement?
Most helpful comment
You could reduce the file size much more. Add the following to
Cargo.tomland docargo build --release.