WASM
assets size for a yew
application in Rust 🦀
Table of content
Few weeks ago I wrote rainbow, a color picker in plain old HTML
/CSS
/JS
. I needed a color palette as quick as possible so I made this tool the quick-and-dirty way.
I then gave this tool some thoughts, rewrote it in Rust
🦀 and called it bifröst. It’s a single page application in WASM
1 and it’s loaded with many more features than rainbow.
Let me tell you something : it’s HEAVY! The first iteration weighed 1.4Mo
. Way too much for such a small application.
This blog post is about the settings and tools to divide its weigh by three.
The problem
Well, 1.4Mo
takes almost 20 seconds to load on a regular 3G connexion according to my favorite browser tools. While I doubt anyone will need this code to run on a mobile, it’s still unreasonable.
The solution
There are two steps for the diet : pick the correct settings for the compiler and then strip the output even further.
Cargo
This is something I usually do for natively executables. And I simply did not think about it for WASM
at all. Shame on me.
[profile.release]
lto = true
opt-level = 'z'
The first line allows for link time optimizations. In other words : please, take your time to compile but make a smaller output.
The second line ask specifically for size optimization and not for speed optimization.
With those two settings only, the weigh dropped down to roughly 700Ko
. It halved the WASM
asset !
More informations about cargo
settings here.
CI/CD
When it comes to native binaries, I use strip
to fully remove any useless piece of data from the file. It turns out their is another tool for WASM
called wasm-opt
in the binaryen
package.
Last time I wrote about hosting a yew
SPA on gitlab
, I used a custom docker
image. All it takes is to add the binaryen
to this very image and use wasm-opt
in the CI/CD
pipeline.
FROM rust:slim
RUN \
rustup target add wasm32-unknown-unknown && \
cargo install --locked trunk && \
apt-get update && \
apt-get install -y binaryen && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
---
build:
stage: build
image: $CI_REGISTRY_IMAGE:latest
script:
- trunk build --release --public-url $CI_PROJECT_NAME
- for wasm in dist/*.wasm; do wasm-opt -Oz $wasm -o $wasm -c; done
variables:
CARGO_HOME: ${CI_PROJECT_DIR}/.cargo
cache:
paths:
- Cargo.lock
- target
- .cargo
artifacts:
paths:
- dist
pages:
stage: deploy
rules:
- if: $CI_COMMIT_TAG != null
needs: [build]
script:
- mv dist public
- cp public/index.html public/404.html
- echo "Deploying to $CI_PAGES_URL."
artifacts:
paths:
- public
And, done ! Down to ~500Ko
.
Conclusion
It literally took 5 lines of configuration to divide the WASM
asset by three.
Now, I know that 500Ko
for such a small application is still a problem. I’ll investigate and write down what I find in another post. And if a fellow reader knows anything about that, it’s possible to drop me a note on Mastodon
and share with the world!
Web Assembly