Edited at

wasmファイルのサイズを小さくするには(Rust編)


はじめに

TL;DL; 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や最適化オプションあたりは自分で設定することもできます。

またこれ以上のバイナリサイズの削減も、きっとできるとは思いますが、それは別の記事としようと思っています。