Rustのバイナリサイズを削減する

## 前提条件
* rustcバージョン `rustc 1.19.0-nightly (e2eaef849 2017-06-11)`
* MacOSX 10.11.6
* ターゲットプロジェクト [mozilla/sccache#c92e0d6](https://github.com/mozilla/sccache/commit/c92e0d63258f7cc8de527cbccd53ea74ac697a16)


## 試したコマンドとその結果

### デバッグビルド

```shell-session
$ cargo build
26884180
```


### リリースビルド

```shell-session
$ cargo build --release
6217224
4502360 (strip)
```
以降特に記載ない限りはリリースビルド前提で、2つの数値の上の数字はコマンド実行後のサイズ、下の数値は`strip`コマンド実施済みのサイズです。


### リンク時シンボルテーブルを含めない
```shell-session
$ cargo rustc --release -- -C link-args=-Wl,-x
5713768
4502360 (strip)
```


### リンク時デバッグ情報を含めない
```shell-session
$ cargo rustc --release -- -C link-args=-Wl,-S
5206568
4502360 (strip)
```

`strip`すると`-C link-args`を付けても付けなくても同じ。


### コンパイラサイズ最適化オプション -Os
```shell-session
$ cargo rustc --release -- -C opt-level=s
5774864
3928720 (strip)
```


### コンパイラサイズ最適化オプション -Oz (-Osよりアグレッシブ)
```shell-session
$ cargo rustc --release -- -C opt-level=z
5845516
3794644 (strip)
```


### LTO(Link Time Optimization)
```shell-session
$ cargo rustc --release -- -C lto
5782092
4372820 (strip)
```
私自身、LTOによる最適化をあまり理解できていない。
リンク時にコンパイル単位を越えた最適化ができるとのことだが、動作を変える可能性があり注意が必要。


### コンパイラサイズ最適化オプション-OsとLTO
```shell-session
$ cargo rustc --release -- -C opt-level=s -C lto
5078180
3542092 (strip)
```


### コンパイラサイズ最適化オプション-OzとLTO
```shell-session
$ cargo rustc --release -- -C opt-level=z -C lto
5134516
3391444 (strip)
```


### panic戦略をpanic=abortにする (プロセスabortさせる)
```shell-session
$ cargo rustc --release -- -C panic=abort
5916312
4243312 (strip)
```
プロセスがOSレベルでAbortシグナル飛ばして死ぬ。あまり実用的ではないかもしれない。


### もろもろ合わせ技
```shell-session
$ cargo rustc --release -- -C opt-level=z -C lto -C link-args=-Wl,-x,-S
3399300
3391444 (strip)
```
`strip`すると、`-C link-args`をつけてもつけなくても同じ。


### ダイナミックリンク
```shell-session
$ cargo rustc --release -- -C prefer-dynamic -C link-args=-Wl,-rpath,$RUST_RPATH
5512288
4119560 (strip)
```
`RUST_RPATH`は`$HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/lib`を指定する

otoolで共有ライブラリを確認

```shell-session
$ otool -L target/release/sccache
target/release/sccache:
	@rpath/libstd-438eba4cd7d88a45.dylib (compatibility version 0.0.0, current version 0.0.0)
	/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 57337.60.3)
	/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1259.11.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)
	/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)
```


### ダイナミックリンク+もろもろ指定
```shell-session
$ cargo rustc --release -- -C opt-level=z -C prefer-dynamic -C link-args=-Wl,-x,-S,-rpath,$RUST_RPATH
3549448
3411744 (strip)
```
libstd以外もリンクするようになるともっとサイズ削減の効果がでるかもしれない。


## まとめ
`cargo rustc --release -- -C opt-level=z -C lto -C link-args=-Wl,-x,-S`が一番サイズ削減できました。
`upx`を使うとさらに削減できると思います。

```
$ upx target/release/sccache
1229464 (strip+upx)
```
今回のケースでは5.9Mから1.2Mと約1/5程になりました。


## おわりに
Linuxでも測定しようかと思っていましたが、力尽きました。


## 参考
* [Rustlog : Why is a Rust executable large?](https://lifthrasiir.github.io/rustlog/why-is-a-rust-executable-large.html)
* [Making executables smaller - The Rust Programming Language Forum](https://users.rust-lang.org/t/making-executables-smaller/5954)