Mengapa Rust executable begitu besar?

153

Baru saja menemukan Rust dan setelah membaca dua bab pertama dari dokumentasi, saya menemukan pendekatan dan cara mereka mendefinisikan bahasa sangat menarik. Jadi saya memutuskan untuk membasahi jari saya dan mulai dengan Hello world ...

Saya melakukannya pada Windows 7 x64, btw.

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

Mengeluarkan cargo builddan melihat hasilnya targets\debugsaya menemukan hasilnya .exe3MB. Setelah beberapa pencarian (dokumentasi bendera baris perintah kargo sulit ditemukan ...) Saya menemukan --releaseopsi dan membuat rilis rilis. Yang mengejutkan saya, ukuran .exe hanya menjadi lebih kecil dengan jumlah yang tidak signifikan: 2.99MB bukannya 3MB.

Jadi, mengakui saya seorang pemula untuk Rust dan ekosistemnya, harapan saya adalah bahwa bahasa Pemrograman Sistem akan menghasilkan sesuatu yang kompak.

Adakah yang bisa menguraikan tentang apa yang dikompilasi oleh Rust, bagaimana mungkin ia menghasilkan gambar sebesar itu dari program 3 liner? Apakah kompilasi ke mesin virtual? Apakah ada perintah strip yang saya lewatkan (info debug di dalam rilis rilis?)? Ada hal lain yang memungkinkan untuk memahami apa yang sedang terjadi?

BitTickler
sumber
4
Saya pikir 3Mb tidak hanya berisi Hello World, tetapi juga semua lingkungan yang dibutuhkan untuk platform. Hal yang sama dapat dilihat dengan Qt. Itu tidak berarti jika Anda menulis program 6-line ukurannya akan menjadi 6 Mb. Itu akan tinggal di 3Mb dan akan tumbuh sangat lambat setelah itu.
Andrei Nikolaenko
8
@AndreiNikolaenko Saya tahu itu. Tapi ini mengisyaratkan bahwa mereka tidak menangani perpustakaan seperti C, menambahkan hanya apa yang diperlukan untuk gambar atau sesuatu yang lain sedang terjadi.
BitTickler
@ user2225104 Lihat jawaban saya, RUST menangani pustaka dengan cara yang sama (atau serupa) dengan C, tetapi secara default C tidak mengkompilasi pustaka statis ke dalam program Anda (setidaknya, pada C ++).
AStopher
1
Apakah ini sudah usang sekarang? Dengan versi rustc 1.35.0 dan tidak ada opsi cli saya mendapatkan exe yang berukuran 137kb. Apakah itu secara otomatis mengkompilasi secara dinamis terkait sekarang atau apakah sesuatu yang lain terjadi sementara itu?
itmuckel

Jawaban:

139

Rust menggunakan tautan statis untuk mengkompilasi program-programnya, artinya semua perpustakaan yang dibutuhkan oleh Hello world!program paling sederhana pun akan dikompilasi ke dalam executable Anda. Ini juga termasuk runtime Rust.

Untuk memaksa Rust menautkan program secara dinamis, gunakan argumen baris perintah -C prefer-dynamic; ini akan menghasilkan ukuran file yang jauh lebih kecil tetapi juga akan membutuhkan pustaka Rust (termasuk runtime-nya) agar tersedia untuk program Anda saat runtime. Ini pada dasarnya berarti Anda harus menyediakannya jika komputer tidak memilikinya, menghabiskan lebih banyak ruang daripada waktu yang dihabiskan oleh program yang terhubung secara statis.

Untuk portabilitas, saya sarankan Anda secara statis menghubungkan perpustakaan Rust dan runtime dengan cara yang telah Anda lakukan jika Anda pernah mendistribusikan program Anda kepada orang lain.

Astopher
sumber
4
@ user2225104 Tidak yakin tentang Cargo, tetapi menurut laporan bug ini di GitHub , sayangnya ini belum memungkinkan.
AStopher
2
Tetapi segera setelah Anda memiliki lebih dari 2 karat yang dapat dieksekusi pada suatu sistem, penautan dinamis akan mulai menghemat ruang Anda ...
binki
15
Saya tidak berpikir tautan statis menjelaskan HELLO-WORLD yang besar. Bukankah seharusnya hanya tautan di bagian perpustakaan yang benar-benar digunakan, dan HELLO-DUNIA hampir tidak menggunakan apa-apa?
MaxB
8
BitTicklercargo rustc [--debug or --release] -- -C prefer-dynamic
Zach Mertes
3
@daboross Terima kasih banyak. Saya telah melacak RFC terkait ini . Sayang sekali karena Rust juga menargetkan pemrograman sistem.
Franklin Yu
62

Saya tidak memiliki sistem Windows untuk dicoba, tetapi di Linux, dunia halo yang dikompilasi secara statis sebenarnya lebih kecil daripada yang setara dengan C. Jika Anda melihat perbedaan besar dalam ukuran, itu mungkin karena Anda menghubungkan Rust yang dapat dieksekusi statis dan C satu secara dinamis.

Dengan tautan dinamis, Anda perlu mempertimbangkan ukuran semua perpustakaan dinamis juga, bukan hanya yang dapat dieksekusi.

Jadi, jika Anda ingin membandingkan apel dengan apel, Anda harus memastikan keduanya dinamis atau keduanya statis. Kompiler yang berbeda akan memiliki default yang berbeda, jadi Anda tidak bisa hanya mengandalkan default kompiler untuk menghasilkan hasil yang sama.

Jika Anda tertarik, inilah hasil saya:

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

Ini dikompilasi dengan gcc (Debian 4.9.2-10) 4.9.2 dan rustc 1.0.0-nightly (d17d6e7f1 2015-04-02) (dibangun 2015-04-03), keduanya dengan opsi default dan dengan -staticuntuk gcc dan -C prefer-dynamicuntuk rustc.

Saya memiliki dua versi C hello world karena saya pikir menggunakan puts()tautan mungkin dalam unit kompilasi yang lebih sedikit.

Jika Anda ingin mencoba mereproduksinya di Windows, berikut adalah sumber yang saya gunakan:

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

Juga, ingatlah bahwa jumlah informasi debug yang berbeda, atau tingkat optimisasi yang berbeda juga akan membuat perbedaan. Tapi saya berharap jika Anda melihat perbedaan besar itu karena tautan statis dan dinamis.

aij
sumber
27
gcc cukup pintar untuk melakukan persis printf -> menempatkan substitusi itu sendiri, itu sebabnya hasilnya identik.
Bluss
6
Pada 2018 jika Anda ingin perbandingan yang adil jangan lupa untuk "menghapus" file executable, karena hello world yang dapat dieksekusi Rust pada sistem saya adalah 5.3MB, tetapi turun menjadi kurang dari 10% ketika Anda menghapus semua simbol debug dan seperti itu.
Matti Virkkunen
@MattiVirkkunen: Masih terjadi pada tahun 2020; ukuran alami tampak lebih kecil (tidak jauh dari 5.3M), tetapi rasio simbol terhadap kode masih cukup ekstrim. Build debug, opsi murni default pada Rust 1.34.0 pada CentOS 7, dilucuti dengan strip -s, turun dari 1.6M ke 190K. Rilis membangun (default ditambah opt-level='s', lto = truedan panic = 'abort'untuk meminimalkan ukuran) turun dari 623K ke 158K.
ShadowRanger
Bagaimana membedakan apel statis dan dinamis? Yang terakhir tidak terdengar sehat.
LF
30

Saat kompilasi dengan Cargo, Anda dapat menggunakan tautan dinamis:

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

Ini secara dramatis akan mengurangi ukuran biner, karena sekarang terkait secara dinamis.

Di Linux, setidaknya, Anda juga dapat menghapus biner simbol menggunakan stripperintah:

strip target/release/<binary>

Ini kira-kira akan membagi dua ukuran sebagian besar binari.

Casper Skern Wilstrup
sumber
8
Hanya beberapa statistik, versi rilis default hello world (linux x86_64). 3,5 M, dengan lebih dinamis 8904 B, dilucuti 6392 B.
Zitrax
30

Untuk ikhtisar semua cara untuk mengurangi ukuran biner Rust, lihat min-sized-rustrepositori.

Langkah-langkah tingkat tinggi saat ini untuk mengurangi ukuran biner adalah:

  1. Gunakan Rust 1.32.0 atau lebih baru (yang tidak termasuk jemallocsecara default)
  2. Tambahkan yang berikut ke 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. Bangun dalam mode rilis menggunakan cargo build --release
  2. Jalankan strippada biner yang dihasilkan.

Ada banyak hal yang dapat dilakukan dengan menggunakan nightlyRust, tetapi saya akan membiarkan informasi min-sized-rustitu karena akan berubah seiring waktu karena penggunaan fitur yang tidak stabil.

Anda juga dapat menggunakan #![no_std]untuk menghapus Rust libstd. Lihat min-sized-rustdetailnya.

phoenix
sumber
-10

Ini adalah fitur, bukan bug!

Anda dapat menentukan versi perpustakaan (dalam file Cargo.toml terkait proyek ) yang digunakan dalam program (bahkan yang implisit) untuk memastikan kompatibilitas versi perpustakaan. Ini, di sisi lain, mensyaratkan bahwa perpustakaan tertentu secara statis dihubungkan dengan yang dapat dieksekusi, menghasilkan gambar run-time yang besar.

Hei, ini bukan 1978 lagi - banyak orang memiliki lebih dari 2 MB RAM di komputer mereka :-)

NPHighview
sumber
9
tentukan versi pustaka [...] mengharuskan pustaka tertentu ditautkan secara statis - tidak, tidak. Ada banyak kode di mana versi pustaka yang tepat terkait secara dinamis.
Shepmaster