Skip to content

Commit 4e740c7

Browse files
authored
Optimize binary with wasm-opt in release mode (#206)
# Objective Closes #196, unblocks <TheBevyFlock/bevy_new_2d#312>. With `wasm-opt`, we can further increase the performance and reduce the file size of the Wasm binary we use for web builds. This speeds ups the app both in-game and the loading times. # Solution As a simple first solution, we add a hard-coded size optimization pass in release mode. In future PRs, we can make this more configurable. To the user, we log the time the optimization took as well as the file size reduction as percentage. This is behind the `wasm-opt` feature flag (currently disabled by default), to give the user a way to turn this off and because this increases compile times of the CLI quite a bit.
1 parent 5d99f41 commit 4e740c7

File tree

9 files changed

+281
-28
lines changed

9 files changed

+281
-28
lines changed

Cargo.lock

+210-28
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ default-run = "bevy"
1414
name = "bevy"
1515
path = "src/bin/main.rs"
1616

17+
[features]
18+
# To optimize the Wasm binaries
19+
# Increases compile times (of the CLI) quite a bit
20+
wasm-opt = ["dep:wasm-opt"]
21+
1722
[dependencies]
1823
# CLI argument parsing
1924
clap = { version = "4.5.16", features = ["derive"] }
@@ -42,3 +47,6 @@ actix-web = "4.9.0"
4247

4348
# Opening the app in the browser
4449
webbrowser = "1.0.2"
50+
51+
# Optimizing Wasm binaries
52+
wasm-opt = { version = "0.116.1", optional = true }

src/build/args.rs

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ impl BuildArgs {
1919
matches!(self.subcommand, Some(BuildSubcommands::Web))
2020
}
2121

22+
/// Whether to build with optimizations.
23+
#[cfg(feature = "wasm-opt")]
24+
pub(crate) fn is_release(&self) -> bool {
25+
self.cargo_args.compilation_args.is_release
26+
}
27+
2228
/// The profile used to compile the app.
2329
pub(crate) fn profile(&self) -> &str {
2430
self.cargo_args.compilation_args.profile()

src/build/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ pub fn build(args: &BuildArgs) -> anyhow::Result<()> {
2828
args.profile(),
2929
)?;
3030
wasm_bindgen::bundle(&bin_target)?;
31+
32+
#[cfg(feature = "wasm-opt")]
33+
if args.is_release() {
34+
crate::web::wasm_opt::optimize_bin(&bin_target)?;
35+
}
3136
} else {
3237
cargo::build::command().args(cargo_args).ensure_status()?;
3338
}

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ pub mod external_cli;
55
pub mod lint;
66
pub mod run;
77
pub mod template;
8+
pub(crate) mod web;

src/run/args.rs

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ impl RunArgs {
1919
matches!(self.subcommand, Some(RunSubcommands::Web(_)))
2020
}
2121

22+
/// Whether to build with optimizations.
23+
#[cfg(feature = "wasm-opt")]
24+
pub(crate) fn is_release(&self) -> bool {
25+
self.cargo_args.compilation_args.is_release
26+
}
27+
2228
/// The profile used to compile the app.
2329
pub(crate) fn profile(&self) -> &str {
2430
self.cargo_args.compilation_args.profile()

src/run/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ pub fn run(args: &RunArgs) -> anyhow::Result<()> {
3838
)?;
3939
wasm_bindgen::bundle(&bin_target)?;
4040

41+
#[cfg(feature = "wasm-opt")]
42+
if args.is_release() {
43+
crate::web::wasm_opt::optimize_bin(&bin_target)?;
44+
}
45+
4146
let port = web_args.port;
4247
let url = format!("http://localhost:{port}");
4348

src/web/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#[cfg(feature = "wasm-opt")]
2+
pub(crate) mod wasm_opt;

src/web/wasm_opt.rs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use std::{fs, path::Path, time::Instant};
2+
3+
use anyhow::Context as _;
4+
5+
use crate::run::BinTarget;
6+
7+
/// Optimize the binary with wasm-opt.
8+
pub(crate) fn optimize_bin(bin_target: &BinTarget) -> anyhow::Result<()> {
9+
let wasm_path = bin_target
10+
.artifact_directory
11+
.clone()
12+
.join(format!("{}_bg.wasm", bin_target.bin_name));
13+
14+
optimize_path(&wasm_path)
15+
}
16+
17+
/// Optimize the Wasm binary at the given path with wasm-opt.
18+
fn optimize_path(path: &Path) -> anyhow::Result<()> {
19+
println!("Optimizing with wasm-opt...");
20+
21+
let start = Instant::now();
22+
let size_before = fs::metadata(path)?.len();
23+
24+
wasm_opt::OptimizationOptions::new_optimize_for_size()
25+
.run(path, path)
26+
.context("failed to optimize with wasm-opt")?;
27+
28+
let size_after = fs::metadata(path)?.len();
29+
let size_reduction = 1. - (size_after as f32) / (size_before as f32);
30+
let duration = start.elapsed();
31+
32+
println!(
33+
" Finished in {duration:.2?}. Size reduced by {:.0}%.",
34+
size_reduction * 100.
35+
);
36+
37+
Ok(())
38+
}

0 commit comments

Comments
 (0)