Rust

cargo-bloatでRustのバイナリサイズの原因を特定する

最近Redditでちょっと話題になっていたcargo-bloatを紹介。

Cargo-bloat

どの関数が重いかがざっくりわかるCargo Subcommand
こんな感じになります。

$ cargo bloat --release -n 10
    Finished ...

  File  .text     Size Name
 39.1%  72.8%   1.5MiB [3469 Others]
  3.5%   6.4% 132.1KiB clap::app::parser::Parser::add_defaults
  2.3%   4.3%  88.7KiB clap::app::parser::Parser::add_env
  1.5%   2.8%  56.9KiB <regex::exec::ExecNoSync<'c> as regex::re_trait::Regu...
  1.3%   2.4%  50.0KiB clap::app::parser::Parser::get_matches_with
  1.3%   2.4%  49.8KiB clap::app::parser::Parser::parse_long_arg
  1.3%   2.4%  49.1KiB clap::app::parser::Parser::parse_short_arg
  1.1%   2.0%  40.2KiB rg::args::ArgMatches::to_args
  0.9%   1.7%  34.5KiB regex_syntax::parser::Parser::parse_expr
  0.9%   1.6%  33.4KiB clap::app::validator::Validator::validate_matched_args
  0.7%   1.3%  25.8KiB encoding_rs::variant::VariantDecoder::decode_to_utf8_raw
 53.8% 100.0%   2.0MiB .text section size, the file size is 3.7MiB

How to Use

Install

まだcrates.ioには置いていないのでgitを介してinstallする感じになる。

cargo install --force --git https://github.com/RazrFalcon/cargo-bloat.git

2017/1/12追記: Publishされました

cargo install cargo-bloat

Command

$ cargo bloat -V
cargo-bloat 0.1.0
$ cargo bloat -h
Find out what takes most of the space in your executable

Usage: cargo bloat [options]

Options:
    -h, --help              Print this message
    -V, --version           Print version info and exit
    --features FEATURES     Space-separated list of features to also build
    --all-features          Build all available features
    --no-default-features   Do not build the `default` feature
    --manifest-path PATH    Path to the manifest to analyze
    --release               Build artifacts in release mode, with optimizations
    --example NAME          Build only the specified example
    --crates                Per crate bloatedness
    --filter CRATE          Filter functions by crate
    --split-std             Split the 'std' crate to original crates like core, alloc, etc.
    --full-fn               Print full function name with hash values
    -n NUM                  Number of lines to show, 0 to show all [default: 20]
    -w, --wide              Do not trim long function names
    -v, --verbose           Use verbose output
    -q, --quiet             No output printed to stdout
    --color WHEN            Coloring: auto, always, never
    --frozen                Require Cargo.lock and cache are up to date
    --locked                Require Cargo.lock is up to date
    -Z FLAG ...             Unstable (nightly-only) flags to Cargo

見慣れないオプションがいくつかある。

--crates                Per crate bloatedness
--filter CRATE          Filter functions by crate
--split-std             Split the 'std' crate to original crates like core, alloc, etc.
--full-fn               Print full function name with hash values
-n NUM                  Number of lines to show, 0 to show all [default: 20]
-w, --wide              Do not trim long function names

使用例

お世話になっているripgrepに使ってみる。

$ git clone https://github.com/BurntSushi/ripgrep
$ cd ripgrep
$ cargo bloat --release
    Compiling ...
    Finished ...

  File  .text     Size Name
 34.5%  64.1%   1.3MiB [3459 Others]
  3.5%   6.4% 132.1KiB clap::app::parser::Parser::add_defaults
  2.3%   4.3%  88.7KiB clap::app::parser::Parser::add_env
  1.5%   2.8%  56.9KiB <regex::exec::ExecNoSync<'c> as regex::re_trait::Regu...
  1.3%   2.4%  50.0KiB clap::app::parser::Parser::get_matches_with
  1.3%   2.4%  49.8KiB clap::app::parser::Parser::parse_long_arg
  1.3%   2.4%  49.1KiB clap::app::parser::Parser::parse_short_arg
  1.1%   2.0%  40.2KiB rg::args::ArgMatches::to_args
  0.9%   1.7%  34.5KiB regex_syntax::parser::Parser::parse_expr
  0.9%   1.6%  33.4KiB clap::app::validator::Validator::validate_matched_args
  0.7%   1.3%  25.8KiB encoding_rs::variant::VariantDecoder::decode_to_utf8_raw
  0.7%   1.2%  25.5KiB globset::GlobSet::new
  0.6%   1.2%  23.7KiB rg::run
  0.5%   0.9%  18.6KiB regex::re_bytes::Regex::find_at
  0.5%   0.9%  18.5KiB <regex::re_trait::Matches<'t, R> as core::iter::itera...
  0.5%   0.9%  17.9KiB ignore::walk::Worker::run
  0.4%   0.8%  15.6KiB rg::app::app
  0.4%   0.7%  15.1KiB clap::app::help::Help::write_arg
  0.4%   0.7%  15.0KiB rg::run_one_thread
  0.4%   0.7%  14.9KiB clap::app::usage::get_required_usage_from
  0.4%   0.7%  13.8KiB clap::app::validator::Validator::validate
 53.8% 100.0%   2.0MiB .text section size, the file size is 3.7MiB

普通に打つとこんな感じ。デフォルトではトップ20が表示され、その他は一つにまとめられる。コマンドラインパーサのclapが目立つ。
Sizeは.textセクションのもの。%はファイル全体に対してと.textセクションの合計に対しての両方を出してくれるようになった。

20は多いので-n 10でトップ10だけ表示することにする。これが上に載せた例。

$ cargo bloat --release -n 10
    Finished ...

  File  .text     Size Name
 39.1%  72.8%   1.5MiB [3469 Others]
  3.5%   6.4% 132.1KiB clap::app::parser::Parser::add_defaults
  2.3%   4.3%  88.7KiB clap::app::parser::Parser::add_env
  1.5%   2.8%  56.9KiB <regex::exec::ExecNoSync<'c> as regex::re_trait::Regu...
  1.3%   2.4%  50.0KiB clap::app::parser::Parser::get_matches_with
  1.3%   2.4%  49.8KiB clap::app::parser::Parser::parse_long_arg
  1.3%   2.4%  49.1KiB clap::app::parser::Parser::parse_short_arg
  1.1%   2.0%  40.2KiB rg::args::ArgMatches::to_args
  0.9%   1.7%  34.5KiB regex_syntax::parser::Parser::parse_expr
  0.9%   1.6%  33.4KiB clap::app::validator::Validator::validate_matched_args
  0.7%   1.3%  25.8KiB encoding_rs::variant::VariantDecoder::decode_to_utf8_raw
 53.8% 100.0%   2.0MiB .text section size, the file size is 3.7MiB

clapが重いのは明らかだが、--cratesオプションを渡して確認する。

$ cargo bloat --release --crates
    Finished ...

  File  .text     Size Name
 15.3%  28.4% 584.0KiB clap
 11.1%  20.7% 426.1KiB [Unknown]
 10.7%  20.0% 410.5KiB std
  6.2%  11.5% 236.5KiB regex
  3.4%   6.4% 131.1KiB regex_syntax
  2.5%   4.7%  96.7KiB ignore
  1.3%   2.5%  50.8KiB globset
  0.8%   1.4%  29.3KiB encoding_rs
  0.5%   0.9%  19.0KiB grep
  0.4%   0.8%  17.1KiB walkdir
  0.3%   0.6%  11.8KiB aho_corasick
  0.2%   0.4%   8.0KiB crossbeam
  0.2%   0.3%   6.0KiB env_logger
  0.2%   0.3%   6.0KiB thread_local
  0.1%   0.2%   5.1KiB termcolor
  0.1%   0.2%   3.2KiB ansi_term
  0.1%   0.1%   2.8KiB vec_map
  0.1%   0.1%   2.1KiB utf8_ranges
  0.1%   0.1%   2.1KiB memchr
  0.1%   0.1%   2.0KiB strsim
 53.8% 100.0%   2.0MiB .text section size, the file size is 3.7MiB

28%をclapが占めているという状態。高機能なだけになかなか重い。

軽量版clapを使う

ここでclapv2.29.1を使うようにcargo updateしてからもう一度実行してみる。

$ cargo bloat --release --crates
    Finished ...

  File  .text     Size Name
 12.2%  24.8% 423.5KiB [Unknown]
 11.7%  23.8% 406.8KiB std
  7.0%  14.3% 244.0KiB clap
  6.8%  13.8% 236.7KiB regex
  3.8%   7.7% 131.1KiB regex_syntax
  2.8%   5.7%  96.7KiB ignore
  1.5%   3.0%  50.8KiB globset
  0.8%   1.7%  29.3KiB encoding_rs
  0.5%   1.1%  19.0KiB grep
  0.5%   1.0%  17.1KiB walkdir
  0.3%   0.7%  11.8KiB aho_corasick
  0.2%   0.5%   8.0KiB crossbeam
  0.2%   0.4%   6.0KiB env_logger
  0.2%   0.3%   6.0KiB thread_local
  0.1%   0.3%   5.1KiB termcolor
  0.1%   0.2%   3.2KiB ansi_term
  0.1%   0.2%   2.8KiB vec_map
  0.1%   0.1%   2.1KiB utf8_ranges
  0.1%   0.1%   2.1KiB memchr
  0.1%   0.1%   2.0KiB strsim
 49.2% 100.0%   1.7MiB .text section size, the file size is 3.4MiB

300Kほど軽くなっている。
実は、clapの開発者がcargo-bloatの結果に触発されて軽量化を行ったらしい。開発途中だけど、こんな感じで参考になりそう。

2017/01/12追加: stdライブラリを分割して表示

1/11夜にオプションがいくつか追加されたので--split-stdを使ってみた。--filterとかは自明なので割愛。

$ cargo bloat --release --crates --split-std
    Finished ...

  File  .text     Size Name
 12.2%  24.8% 423.5KiB [Unknown]
  7.0%  14.3% 244.0KiB clap
  6.8%  13.8% 236.7KiB regex
  5.5%  11.2% 191.5KiB core
  3.8%   7.7% 131.1KiB regex_syntax
  3.4%   7.0% 119.6KiB std
  2.8%   5.7%  96.7KiB ignore
  2.6%   5.3%  91.4KiB alloc
  1.5%   3.0%  50.8KiB globset
  0.8%   1.7%  29.3KiB encoding_rs
  0.5%   1.1%  19.0KiB grep
  0.5%   1.0%  17.1KiB walkdir
  0.3%   0.7%  11.8KiB aho_corasick
  0.2%   0.5%   8.0KiB crossbeam
  0.2%   0.4%   6.0KiB env_logger
  0.2%   0.3%   6.0KiB thread_local
  0.1%   0.3%   5.1KiB termcolor
  0.1%   0.2%   3.2KiB std_unicode
  0.1%   0.2%   3.2KiB ansi_term
  0.1%   0.2%   2.8KiB vec_map
 49.2% 100.0%   1.7MiB .text section size, the file size is 3.4MiB

stdライブラリがcoreallocなどのcrateに分割されて表示されているのがわかる。

注意点

  • RustではなくCで書かれたライブラリ等は[Unknown]にまとめられる
  • Windows未対応