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


153

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

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

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

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

したがって、私がRustとそのエコシステムの初心者であることを告白すると、システムプログラミング言語が何かコンパクトなものを生み出すことが期待されていました。

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


4
3MbにはHello Worldだけでなく、プラットフォームに必要なすべての環境も含まれていると思います。同じことがQtでも見られます。これは、6行のプログラムを作成した場合、サイズが6 Mbになるという意味ではありません。それは3Mbにとどまり、その後は非常にゆっくりと成長します。
Andrei Nikolaenko 2015

8
@AndreiNikolaenko私はそれを知っています。しかし、これは、Cのようにライブラリを処理せず、イメージに必要なものだけを追加するか、何か他のことが行われていることを示唆しています。
BitTickler 2015年

@ user2225104私の答えを参照してください。RUSTはCと同じ(または同様の)ライブラリを処理しますが、デフォルトではCは静的ライブラリをプログラムにコンパイルしません(少なくともC ++では)。
AStopher 2015年


1
これは今時代遅れですか?rustcバージョン1.35.0でcliオプションがない場合、サイズが137kbのexeを取得します。動的にリンクされて自動的にコンパイルされるのですか、それともその間に他のことが起こりましたか?
itmuckel

回答:


139

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

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

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


4
@ user2225104 Cargoについては不明ですが、GitHubのこのバグレポートによると、残念ながらこれはまだ不可能です。
AStopher 2015年

2
しかし、システムに2つ以上のrust実行可能ファイルがあるとすぐに、動的リンクによりスペースが節約されます…
binki

15
静的リンクが巨大なHELLO-WORLDを説明しているとは思いません。実際に使用されているライブラリの一部のみをリンクするのではなく、HELLO-WORLDは実質的に何も使用しませんか?
MaxB

8
BitTicklercargo rustc [--debug or --release] -- -C prefer-dynamic
Zach Mertes

3
@daborossありがとうございます。この関連RFCを追跡しています。Rustはシステムプログラミングもターゲットにしているため、これは本当に残念です。
フランクリンユー

62

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

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

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

あなたが興味があるなら、これが私の結果です:

-rw-r--r-- 1 aij aij 63 Apr 5 14:26 printf.c
-rwxr-xr-x 1 aij aij 6696 Apr 5 14:27 printf.dyn
-rwxr-xr-x 1 aij aij 829344 4月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 Apr 5 14:27 puts.dyn
-rwxr-xr-x 1 aij aij 829344 Apr 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 Apr 5 14:28 rust.static

これらは、デフォルトのオプションと-staticfor gccおよび-C prefer-dynamicfor rustc。

CのHello Worldには2つのバージョンがありましたputs()。使用するリンクの数が少ないコンパイルユニットになると考えたためです。

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!");
}

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


27
gccは、printfを正確に実行するのに十分スマートです->置換自体を配置するため、結果は同じです。
ブラス

6
2018年現在、公平な比較が必要な場合は、実行可能ファイルを「ストリップ」することを忘れないでください。HelloWorld Rust実行可能ファイルはシステムでなんと5.3MBですが、すべてのデバッグシンボルを削除すると、その10%未満に低下します。そのような。
Matti Virkkunen、

@MattiVirkkunen:2020年もそうです。自然なサイズは小さいように見えますが(5.3Mに近いところはありません)、コードに対するシンボルの比率は依然としてかなり極端です。CentOS 7のRust 1.34.0の純粋なデフォルトオプションであるデバッグビルドは、strip -s1.6Mから190Kに削除されました。リリースビルド(デフォルトはプラスopt-level='s'lto = trueおよびpanic = 'abort'サイズを最小にする)は、623Kから158Kに低下します。
ShadowRanger

静的リンゴと動的リンゴを区別する方法は?後者は健康的に聞こえません。
LF

30

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

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

これにより、バイナリが動的にリンクされるようになるため、バイナリのサイズが劇的に減少します。

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

strip target/release/<binary>

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


8
ほんの一部の統計、hello worldのデフォルトリリースバージョン(linux x86_64)。3.5 Mは、好む動的8904 Bと、6392 B.剥離
Zitrax

30

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結果のバイナリで実行します。

nightlyRust を使用して実行できることは他にもありますがmin-sized-rust、不安定な機能を使用しているために時間とともに変化するため、その情報はそのままにしておきます。

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


-10

これは機能であり、バグではありません。

ライブラリのバージョンの互換性を確保するために、プログラムで使用されるライブラリのバージョン(プロジェクトに関連付けられているCargo.tomlファイル内)を指定できます(暗黙のものも含む)。一方、これには、特定のライブラリを実行可能ファイルに静的にリンクして、大きなランタイムイメージを生成する必要があります。

ねえ、それはもはや1978年ではありません-多くの人々は彼らのコンピュータに2 MB以上のRAMを持っています:-)


9
ライブラリのバージョンを指定するには[...]特定のライブラリを静的にリンクする必要があります —いいえ、そうではありません。ライブラリの正確なバージョンが動的にリンクされているコードはたくさんあります。
シェプマスター2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.