##TL;DR;
wasm-packを使いましょう。
はじめに
こちらの記事で、Rustでwasm32のバイナリを出力できるようになりました。こちらの記事で作成されたhello-world.wasm
のサイズは875KBでした。これを小さくしていくのが、この記事の目的です。
ここに書いたことの大半は、wasm-packが面倒を見てくれます。手でいろいろやりたい、という人向けの記事となっています。
releaseビルド
Rustのビルドには、debugビルドとreleaseビルドがあります。
debugは名前の通り、デバッグ用のビルドです。最適化はあまりされておらず、デバッグ用の情報も追加されています。cargo
コマンドでビルドした際のデフォルトは、debugビルドです。
一方releaseビルドは、最適化がなされ、デバッグ用の情報も追加されていません(通常は)。--release
オプションを指定してビルドすると、releaseビルドされたバイナリを作成できます。
% ls target/wasm32-unknown-unknown/release
build examples hello-world.wasm native
deps hello-world.d incremental
で、releaseビルドをでは、どれくらいサイズが小さくなってるのかというと、実はあんまり小さくなっていません。
ビルド | サイズ |
---|---|
debug | 875K |
release | 866K |
より小さくするために、最適化オプションを変更してみることにします。
lto オプションと最適化レベルの指定
Cargo.toml
には最適化レベルを指定できます。何が書けるかはThe Rust Programming Language BookのThe manifest formatの節に説明があります。
このファイルに次の3行を追加します。
[profile.release]
lto = true
opt-level = "z"
ltoオプションにtrue
を指定することで、ライブラリのリンク時も最適化を行います。またopt-level
は最適化レベルを表します。z
は、より積極的にバイナリサイズを小さくするように最適化を行います。
結果は次のようになりました。
ビルド | サイズ |
---|---|
debug | 875K |
release | 866K |
lto + -Oz | 273K |
だいぶ減りました。もう少し減らすために、何が原因かを調査します。
コードサイズプロファイラ
コードサイズを増やしている原因を調査するために、コードサイズプロファイラと呼ばれるツールを利用することにします。今回はtwiggyと呼ばれるツールを利用します。
tiwggyは、次のようにcargo
コマンドを使ってインストールできます。
% cargo install twiggy
twiggyはいくつかのサブコマンドがありますが、top
を使うと次のように、どの項目が、どれだけのサイズなのかをランキングで表示できます。
Shallow Bytes | Shallow % | Item |
---|---|---|
82059 | 29.37% | custom section '.debug_info' |
76288 | 27.30% | custom section '.debug_line' |
46774 | 16.74% | custom section '.debug_str' |
25201 | 9.02% | custom section '.debug_ranges' |
7738 | 2.77% | "function names" subsection |
7595 | 2.72% | data[0] |
3315 | 1.19% | dlmalloc::dlmalloc::Dlmalloc::malloc::h8a37a103d2401d9d |
1656 | 0.59% | main |
1514 | 0.54% | std::io::Write::write_all::hfd4c5b414ccb1671 |
1399 | 0.50% | <str as core::fmt::Debug>::fmt::hda75deb2d8ed9e81 |
23210 | 8.31% | ... and 251 more. |
276749 | 99.04% | Σ [261 Total Rows] |
なおこの結果は、次のコマンドは実行して得ました。top
はサイズのランキングを出すサブコマンドす。-n
オプションを加えることで、何位までを出力するかを決められます。
% twiggy top -n 10 target/wasm32-unknown-unknown/release/hello-world.wasm
デバッグ情報の削除
上記の結果を見ると、デバッグ情報が大きなサイズを取っていることがわかりました。これを削除します。削除には、wasm-snipというツールを利用しました。
wasm-snipのインストールと、デバッグ情報の削除
Cargoコマンドでインストールできます。
% cargo install wasm-snip
不要なコードの削除は、次のように行います。-o
オプションには、不要な情報が削除されたwasmファイルのパスを指定します。
% wasm-snip -o hello-world-snipped.wasm target/wasm32-unkown-unknown/release/hello-world.wasm
結果は、次のようになりました。もとの20分の1程度には小さくなりました。
ビルド | サイズ |
---|---|
debug | 875K |
release | 866K |
lto + -Oz | 273K |
wasm-snip | 45K |
どこにデバッグ情報があるのか?
WebAssebmlyにはCustom Sectionあります。このセクションは、モジュールの振る舞いには影響は与えないが、処理系にとって有用な情報を書く場所として定義されています。
デバッグに必要な情報も、振る舞いには影響を与えないが処理系にとって有用な情報なので、ここに書かれます。
まとめ
- Cargo.tomlでLTOを有効にすること
- コンパイラの最適化オプションを変えて、
z
にすること - wasm-snipをつかって、不要なコードを削除すること
以上が、この記事のまとめです。
はじめにの節にも書きましたが、ここに書かれていることの大半はwasm-packが面倒を見てくれます。ただ、LTSや最適化オプションあたりは自分で設定することもできます。
またこれ以上のバイナリサイズの削減も、きっとできるとは思いますが、それは別の記事としようと思っています。