Чому виконуючі іржі такі величезні?


153

Щойно знайшовши Руста і прочитавши перші два глави документації, я вважаю підхід і те, як вони визначили мову особливо цікавими. Тому я вирішив змочити пальці і почав з Hello world ...

Я робив це на Windows 7 x64, btw.

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

Видаючи cargo buildі дивлячись на результат, targets\debugя виявив, що отриманий .exe3MB. Після деяких пошуків (документацію вантажів прапор командного рядка важко знайти ...) я знайшов --releaseваріант і створив версію версії. На мій подив, розмір .exe став лише меншим на незначну суму: 2,99 Мб замість 3 МБ.

Тож, зізнавшись, я новачок у Расті та його екосистеми, я сподівався, що мова програмування систем створить щось компактне.

Хтось може детальніше розібратися з тим, до чого збирається Rust, як це можливо, вона створює такі величезні зображення з 3-х лайнерної програми? Це компіляція до віртуальної машини? Чи є команда стриппу, яку я пропустив (інформація про налагодження у складі релізу?)? Що ще, що могло б зрозуміти, що відбувається?


4
Я думаю, що 3Mb містить не тільки Hello World, але і все необхідне середовище для платформи. Те саме можна побачити і з Qt. Це не означає, що якщо ви пишете 6-рядкову програму, розмір стане 6 Мб. Він залишиться на рівні 3 Мбіт і зросте дуже повільно після цього.
Андрій Ніколаєнко

8
@AndreiNikolaenko Мені це відомо. Але це натякає, що або вони не обробляють бібліотеки, як це робить C, додаючи лише те, що потрібно для зображення, або що щось інше відбувається.
BitTickler

@ user2225104 Дивіться мою відповідь, RUST обробляє бібліотеки так само (або аналогічно), як і C, але за замовчуванням C не збирає статичні бібліотеки у вашу програму (принаймні, на C ++).
AStopher


1
Це зараз застаріло? З rustc версією 1.35.0 і без варіантів cli я отримую exe розміром 137kb. Він автоматично компілює динамічно пов'язані зараз чи щось тим часом сталося тим часом?
itmuckel

Відповіді:


139

Rust використовує статичне посилання для компіляції своїх програм, тобто всі бібліотеки, необхідні навіть найпростішій Hello world!програмі, будуть зібрані у ваш виконуваний файл. Сюди входить також іржі.

Щоб змусити Руста динамічно зв’язувати програми, використовуйте аргументи командного рядка -C prefer-dynamic; це призведе до значно меншого розміру файлів, але також вимагатиме, щоб бібліотеки Rust (включаючи його час виконання) були доступні вашій програмі під час виконання програми. Це по суті означає, що вам потрібно буде їх надати, якщо на комп’ютері їх немає, зайнявши більше місця, ніж займає ваша первісна статично пов’язана програма.

Для портативності я рекомендую вам статично зв’язувати бібліотеки Rust та час виконання так, як ви робили, якщо ви коли-небудь розповсюджували свої програми іншим.


4
@ user2225104 Не впевнені у вантажі, але згідно з повідомленням про помилку на GitHub , на жаль, це ще не можливо.
AStopher

2
Але як тільки у вас буде більше 2-х виконуваних іржі в системі, динамічне посилання почне економити вам місце…
binki

15
Я не думаю, що статичні зв'язки пояснюють величезний ХЕЛО-СВІТ. Чи не повинно воно посилатися лише на частини бібліотек, які фактично використовуються, а HELLO-WORLD практично нічого не використовує?
MaxB

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

3
@daboross Дякую дуже Я відстежував цей пов'язаний RFC . Дуже шкода, оскільки Руст також націлений на системне програмування.
Франклін Ю

62

У мене немає жодних систем Windows, які можна було б приміряти, але в Linux статично складений світ привіт привіт Rust насправді менший, ніж еквівалент C. Якщо ви бачите величезну різницю в розмірах, це, мабуть, тому, що ви пов'язуєте виконуваний файл Rust статично, а С - динамічно.

При динамічному зв’язуванні вам потрібно враховувати і розмір усіх динамічних бібліотек, а не лише виконуваних файлів.

Отже, якщо ви хочете порівняти яблука з яблуками, вам потрібно переконатися, що обидва є динамічними, або обидва - статичними. У різних компіляторів будуть різні за замовчуванням, тому ви не можете просто розраховувати на параметри компілятора, щоб отримати той самий результат.

Якщо ви зацікавлені, ось мої результати:

-rw-r - r-- 1 aij aij 63 квітня 5 14:26 printf.c
-rwxr-xr-x 1 aij aij 6696 5 квітня 14:27 printf.dyn
-rwxr-xr-x 1 aij aij 829344 5 квітня 14:27 printf.static
-rw-r - r-- 1 aij aij 59 квіт. 5 14:26 puts.c
-rwxr-xr-x 1 aij aij 6696 5 квітня 14:27 puts.dyn
-rwxr-xr-x 1 aij aij 829344 5 квітня 14:27 puts.static
-rwxr-xr-x 1 aij aij 8712 квітня 5 14:28 rust.dyn
-rw-r - r-- 1 aij aij 46 квітня 14 14:09 rust.rs
-rwxr-xr-x 1 aij aij 661496 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), як із параметрами за замовчуванням, так і з -staticgcc та -C prefer-dynamicдля іржа.

У мене було дві версії світу привіт С, тому що я думав, що використання 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 рік, якщо ви хочете чесного порівняння, пам’ятайте, що «зніміть» виконувані файли, оскільки привіт, світ, який виконується у моїй системі, є надзвичайними 5,3 МБ, але знижується менше ніж на 10% від того, коли ви видаляєте всі символи налагодження та таких.
Матті Вірккунен

@MattiVirkkunen: Все ще справа в 2020 році; природний розмір здається меншим (ніде близько 5,3 М), але співвідношення символів до коду все ще досить екстремальне. Збірка налагодження, суто параметри за замовчуванням на Rust 1.34.0 на CentOS 7, позбавлений strip -s, падає з 1.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
Лише деякі статистичні дані за версією привітного світу версії за замовчуванням (linux x86_64). 3,5 М, з
перевагою

30

Для огляду всіх способів зменшити розмір бінарного файлу іржі див. 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]можете видалити іржі libstd. Детальніше min-sized-rustдив.


-10

Це особливість, а не помилка!

Ви можете вказати версії бібліотеки (у пов'язаному з проектом файлі Cargo.toml ), використовувані в програмі (навіть неявні) для забезпечення сумісності версій бібліотеки. З іншого боку, це вимагає, щоб певна бібліотека була статично пов'язана з виконуваним файлом, створюючи великі зображення під час виконання.

Гей, це вже не 1978 рік - багато людей мають більше 2 Мб оперативної пам’яті на своїх комп’ютерах :-)


9
Вказати версії бібліотеки [...] вимагає, щоб певна бібліотека була статично пов'язана - ні, це не так. Існує безліч кодів, де точні версії бібліотек динамічно пов'язані.
Шепмейстер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.