web-dev-qa-db-ja.com

なぜRust実行可能ファイルはそんなに大きいのですか?

Rustを見つけて、ドキュメントの最初の2つの章を読んだだけで、言語を定義するアプローチと方法が特に興味深いことがわかりました。だから私は私の指を濡らすことにし、Hello worldで始めました...

私はWindows 7 x64でそうしました。

fn main() {
    println!("Hello, world!");
}

cargo buildを発行し、targets\debugの結果を見ると、結果の.exeが3MBであることがわかりました。いくつかの検索の後(貨物のコマンドラインフラグのドキュメントを見つけるのは難しい...)--releaseオプションを見つけ、リリースビルドを作成しました。驚いたことに、.exeのサイズは、3MBではなく2.99MBというわずかな量だけ小さくなりました。

だから、私はRustとそのエコシステムの初心者であると告白し、システムプログラミング言語がコンパクトな何かを生み出すだろうと期待していたでしょう。

誰がRustがコンパイルするのか、3ライナープログラムからこのような巨大な画像をどのように生成できるかについて詳しく説明できますか?仮想マシンへのコンパイルですか?見逃したストリップコマンドはありますか?(リリースビルド内のデバッグ情報?)何が起こっているのかを理解できるようにする他の何かはありますか?

118
BitTickler

Rustは静的リンクを使用してプログラムをコンパイルします。つまり、最も単純なHello world!プログラムでも必要なすべてのライブラリが実行可能ファイルにコンパイルされます。これには、Rustランタイムも含まれます。

Rustにプログラムを動的にリンクさせるには、コマンドライン引数-C prefer-dynamic;を使用します。これにより、ファイルサイズは非常に小さくなりますしかしは、Rustライブラリ(ランタイムを含む)が実行時のプログラム。これは本質的に、コンピューターにそれらがない場合に提供する必要があり、元の静的にリンクされたプログラムが占有するよりもmoreスペースを占有することを意味します。

移植性のために、プログラムを他の人に配布する場合は、Rustライブラリとランタイムを静的にリンクすることをお勧めします。

112
AStopher

試用するWindowsシステムはありませんが、Linuxでは、静的にコンパイルされたRust hello worldは実際には同等のCよりも小さいです。サイズに大きな違いがある場合は、おそらくRust実行可能ファイルを静的にリンクし、C実行可能ファイルを動的にリンクしています。

動的リンクでは、実行可能ファイルだけでなく、すべての動的ライブラリのサイズも考慮する必要があります。

したがって、リンゴとリンゴを比較する場合は、両方が動的であるか、両方が静的であることを確認する必要があります。コンパイラーごとにデフォルト値が異なるため、コンパイラーのデフォルト値だけで同じ結果を生成することはできません。

興味があるなら、ここに私の結果があります:

-rw-r--r-- 1 aij aij 63 4月5 14:26 printf.c 
-rwxr-xr-x 1 aij aij 6696 4月5 14:27 printf.dyn 
-rwxr-xr-x 1 aij aij 829344 Apr 5 14:27 printf.static 
-rw-r--r-- 1 aij aij 59 Apr 5 14:26 puts.c 
-rwxr-xr-x 1 aij aij 6696 4月5 14:27 puts.dyn 
-rwxr-xr-x 1 aij aij 829344 4月5 14:27 puts.static 
- rwxr-xr-x 1 aij aij 8712 Apr 5 14:28 Rust.dyn 
-rw-r--r-- 1 aij aij 46 Apr 5 14:09 Rust.rs 
-rwxr -xr-x 1 aij aij 661496 4月5日14:28 Rust.static 

これらはgcc(Debian 4.9.2-10)4.9.2およびrustc 1.0.0-nightly(d17d6e7f1 2015-04-02)(ビルド2015-04-03)でコンパイルされ、デフォルトオプションと-static gccの場合と-C prefer-dynamic rustcの場合。

puts()を使用するとコンパイル単位が少なくなる可能性があると考えたため、2つのバージョンのC helloワールドがありました。

Windowsでそれを再現したい場合、私が使用したソースは次のとおりです。

printf.c:

#include <stdio.h>
int main() {
  printf("Hello, world!\n");
}

puts.c:

#include <stdio.h>
int main() {
  puts("Hello, world!");
}

Rust.rs

fn main() {
    println!("Hello, world!");
}

また、デバッグ情報の量や最適化レベルが異なると違いが生じることにも注意してください。しかし、大きな違いが見られるのは、静的リンクと動的リンクが原因だと思います。

53
aij

Cargoでコンパイルする場合、動的リンクを使用できます。

cargo rustc --release -- -C prefer-dynamic

これにより、バイナリが動的にリンクされるため、バイナリのサイズが大幅に縮小されます。

少なくともLinuxでは、stripコマンドを使用してシンボルのバイナリを削除することもできます。

strip target/release/<binary>

これにより、ほとんどのバイナリのサイズがほぼ半分になります。

23

Rustバイナリのサイズを縮小するすべての方法の概要については、 min-sized-Rust リポジトリを参照してください。

バイナリサイズを削減する現在の高レベルの手順は次のとおりです。

  1. Rust 1.32.0以降を使用します(デフォルトではjemallocは含まれません)
  2. 以下を Cargo.toml に追加します
[profile.release]
opt-level = 'z'     # Optimize for size.
lto = true          # Enable Link Time Optimization
codegen-units = 1   # Reduce number of codegen units to increase optimizations.
panic = 'abort'     # Abort on panic
  1. cargo build --releaseを使用してリリースモードでビルドする
  2. 結果のバイナリで strip を実行します。

nightly Rustを使用してできることはほかにもありますが、不安定な機能の使用により時間とともに変化するため、この情報は min-sized-Rust に残しておきます。

#![no_std]を使用して、Rustのlibstdを削除することもできます。詳細については min-sized-Rust をご覧ください。

11
phoenix