-
I'm working on a Rust/wasm32 app.
-
I'm now at the point where ls -l deploy/*.wasm (release mode, non debug mode) is hitting 7MB (not for entire directory, for the single wasm file).
-
Besides manually removing creates one by one, trying to make the code recompile, is there anyway to figure out what crates are causing the wasm to balloon in size?
-
There are no non-trivial include-string/include-bytes.
There's a couple of sections in the Rust WASM book about shrinking binary size - one in the tutorial, and one in the reference. If you've not tried the steps from there, that'd be a good start.
*.wasm binary size went from 7MB to 1MB. Thanks!
Side note: If you care about deploying this on a website, make sure to compare the gzipped sizes!
Is there a way to get 'cargo web deploy' to automatically handle this for me? If not, what is a recommended guide? (This notion of gzipping wasm is new to me.)
Typically you don't gzip it yourself -- webservers will gzip it, often on the fly, to deliver it to clients. So the client may download a lot less than 1MB.
I've been playing around with making very small wasm binaries recently, and I have a few other suggestions on how to investigate your binary size.
You likely know this already, but for people who find this thread from a search engine: Rust wasm binaries are not inherently large. My smallest useful deployed one is about 200 bytes. I gather some other toolchains targeting wasm right now produce big binaries; Rust isn't one of them.
So, once you have a wasm module, how do you tell why it's large? The two things I'd look for are:
- Large initialized data sections.
- Suspiciously large clusters of functions for a single crate.
The wasm binutils (wabt) don't provide great support for the second use case, but the first one is okay:
$ wasm-objdump -x -j data your_wasm_module.wasm | less
The init= at the top of each chunk shows you the size in bytes. If the size of any section, or all the sections together, approaches your problematic module size, then you've found your culprit. Tying them back to their crates of origin is harder; squint at the data and see if anything jumps out, like crate names in str literals.
For identifying large code, here's a script that generates a simple report. Use the script like this:
$ wasm-objdump -x your_wasm_module.wasm | ruby report.rb
The report will attempt to summarize usage by both function name and crate name. (I say "attempt" because the symbol parser is kind of a hack, but it works for the modules I've tried.) Example output:
# By func name
... many lines omitted ...
1403 <str as core::fmt::Debug>::fmt::hda75deb2d8ed9e81
1487 wasm_game_of_life::conway::step::h0e8b342676c7bf62
3303 dlmalloc::dlmalloc::Dlmalloc::malloc::h8a37a103d2401d9d
# By crate
4 js_sys
410 console_error_panic_hook
892 wasm_bindgen
2067 alloc
2526 wasm_game_of_life
4237 std
4902 unknown
6697 dlmalloc
11643 core
Script (in Ruby, because I'm guessing you have Ruby installed):
#!/usr/bin/env ruby
func_names = {}
func_crates = {}
size_by_name = {}
size_by_crate = {}
STDIN.each_line { |line|
line.strip!
if line =~ /func\[([0-9]+)\] sig=[0-9]+ <(.*)>$/
idx = $1.to_i
name = $2
if name =~ /^([a-zA-Z0-9_]+)::/
crate = $1
elsif name =~ /^<([a-zA-Z0-9_]+)::.* as /
crate = $1
else
crate = 'unknown'
end
func_names[idx] = name
func_crates[idx] = crate
elsif line =~ /func\[([0-9]+)\] size=([0-9]+)/
idx = $1.to_i
size = $2.to_i
size_by_name[func_names[idx]] = size
size_by_crate[func_crates[idx]] ||= 0
size_by_crate[func_crates[idx]] += size
end
}
puts "# By func name"
size_by_name.each.sort { |a, b| a[1] <=> b[1] }.each { |n, size|
printf("% 10d %s\n", size, n)
}
puts "# By crate"
size_by_crate.each.sort { |a, b| a[1] <=> b[1] }.each { |n, size|
printf("% 10d %s\n", size, n)
}
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.