Бороздя просторы интернета вы наверняка уже успели услышать про Rust. После всех красноречивых отзывов и расхваливаний вы, конечно же, не смогли не потрогать это чудо. Первая программа выглядела не иначе как:
fn main() {
println!("Hello, world!");
}
Скомпилировав получим соответствующий исполняемый файл:
$ rustc hello.rs
$ du -h hello
632K hello
632 килобайт для простого принта?! Rust позиционируется как системный язык, который имеет потенциал для замены C/C++, верно? Так почему бы не проверить аналогичную программу на ближайшем конкуренте?
$ cat hello.c
#include <stdio.h>
int main() {
printf("Hello, World!\n");
}
$ gcc hello.c -ohello
$ du -h hello
6.7K hello
Более безопасные и громоздкие iostream-ы C++ выдают не сильно иной результат:
$ cat hello.cpp
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
}
$ g++ hello.cpp -ohello
$ du -h hello
8.3K hello
Флаги -O3/-Os практически не меняют конечного размера
Так что не так с Rust?
Кажется, что необычный размер исполняемых файлов Rust интересует много кого, и вопрос этот совершенно не нов. Взять, к примеру, этот вопрос на stackoverflow, или множество других. Даже немного странно, что все еще не было статей или каких-либо заметок описывающих эту проблему.
Все примеры были перетестированы на Rust 1.11.0-nightly (1ab87b65a 2016-07-02) на Linux 4.4.14 x86_64 без использования cargo и stable-ветки в отличии от оригинальной статьи.
Уровень оптимизации
Любой опытный программист конечно же воскликнет, что дебаг билд на то и дебаг, и нередко его размер значительно превышает релиз-версию. Rust в данном случае не исключение и достаточно гибко позволяет настраивать параметры сборки. Уровни оптимизации аналогичны gcc, задать его можно с помощью параметра -C opt-level=x, где вместо x число от 0-3, либо s для минимизации размера. Ну что же, посмотрим что из этого выйдет:
$ rustc helloworld.rs -C opt-level=s
$ du -h helloworld
630K helloworld
Что удивительно каких-либо значительных изменений нет. На самом деле это происходит из-за того, что оптимизация применяется лишь к пользовательскому коду, а не к уже скомпонованной среде исполнения Rust.
Оптимизация линковки (LTO)
Rust по стандартному поведению к каждому исполняемому файлу линкует всю свою стандартную библиотеку. Так что мы можем избавиться и от этого, ведь глупый линковщик не понимает, что нам не очень нужно взаимодействие с сетью.
На самом деле есть хорошая причина для такого поведения. Как вы наверное знаете языки C и C++ компилируют каждый файл по отдельности. Rust же поступает немного иначе, где единицей компиляции выступает крейт (crate). Не трудно догадаться, что вызов функций из других файлов компилятор не сможет оптимизировать, так как он попросту работает с одним большим файлом.
Изначально в C/C++ компилятор производил оптимизацию независимо каждого файла. Со временем появилась технология оптимизации при линковке. Хоть это и стало занимать значительно больше времени, зато в результате получались исполняемые файлы куда лучше, чем раньше. Посмотрим как изменит положение дел эта функциональность в Rust:
$ rustc helloworld.rs -C opt-level=s -C lto
$ du -h helloworld
604K helloworld
Так что же внутри?
Первое, чем наверное стоит воспользоваться — это небезызвестная утилита strings из набора GNU Binutils. Вывод ее достаточно большой (порядка 6 тыс. строк), так что приводить его полностью не имеет смысла. Вот самое интересное:
$ strings helloworld
capacity overflow
attempted to calculate the remainder with a divisor of zero
<jemalloc>: Error in atexit()
<jemalloc>: Error in pthread_atfork()
DW_AT_member
DW_AT_explicit
_ZN4core3fmt5Write9write_fmt17ha0cd161a5f40c4adE # или core::fmt::Write::write_fmt::ha0cd161a5f40c4ad
_ZN4core6result13unwrap_failed17h072f7cd97aa67a9cE # или core::result::unwrap_failed::h072f7cd97aa67a9c
На основе этого результата можно сделать несколько выводов:
— К исполняемым файлам Rust статически линкуется вся стандартная библиотека.
— Rust использует jemalloc вместо системного аллокатора
— К файлам также статически линкуется библиотека libbacktrace, которая нужна для трассировки стека
Все это, как вы понимаете, для обычного println не очень то и нужно. Значит самое время от них всех избавиться!
Отладочные символы и libbacktrace
Начнем с простого — убрать из исполняемого файла отладочные символы.
$ strip hello
# du -h hello
356K helloworld
Очень неплохой результат, почти половину исходного размера занимают отладочные символы. Хотя в этом случае удобочитаемого вывода при ошибках, вроде panic! нам не получить:
$ cat helloworld.rs
fn main() {
panic!("Hello, world!");
}
$ rustc helloworld.rs && RUST_BACKTRACE=1 ./helloworld
thread 'main' panicked at 'Hello, world!', helloworld.rs:2
stack backtrace:
1: 0x556536e40e7f - std::sys::backtrace::tracing::imp::write::h6528da8103c51ab9
2: 0x556536e4327b - std::panicking::default_hook::_$u7b$$u7b$closure$u7d$$u7d$::hbe741a5cc3c49508
3: 0x556536e42eff - std::panicking::default_hook::he0146e6a74621cb4
4: 0x556536e3d73e - std::panicking::rust_panic_with_hook::h983af77c1a2e581b
5: 0x556536e3c433 - std::panicking::begin_panic::h0bf39f6d43ab9349
6: 0x556536e3c3a9 - helloworld::main::h6d97ffaba163087d
7: 0x556536e42b38 - std::panicking::try::call::h852b0d5f2eec25e4
8: 0x556536e4aadb - __rust_try
9: 0x556536e4aa7e - __rust_maybe_catch_panic
10: 0x556536e425de - std::rt::lang_start::hfe4efe1fc39e4a30
11: 0x556536e3c599 - main
12: 0x7f490342b740 - __libc_start_main
13: 0x556536e3c268 - _start
14: 0x0 - <unknown>
$ strip helloworld && RUST_BACKTRACE=1 ./helloworld
thread 'main' panicked at 'Hello, world!', helloworld.rs:2
stack backtrace:
1: 0x55ae4686ae7f - <unknown>
...
11: 0x55ae46866599 - <unknown>
12: 0x7f70a7cd9740 - __libc_start_main
13: 0x55ae46866268 - <unknown>
14: 0x0 - <unknown>
Вытащить целиком libbacktrace из линковки без последствий не получится, он сильно связан со стандартной библиотекой. Но зато размотка для паники из libunwind нам не нужна, и мы можем ее выкинуть. Незначительные улучшения мы все таки получим:
$ rustc helloworld.rs -C lto -C panic=abort -C opt-level=s
$ du -h helloworld
592K helloworld
Убираем jemalloc
Компилятор Rust стандартной сборки чаще всего использует jemalloc, вместо системного аллокатора. Изменить это поведение очень просто: нужно всего лишь вставить макро и импортировать нужный крейт аллокатора.
#![feature(alloc_system)]
extern crate alloc_system;
fn main() {
println!("Hello, world!");
}
$ rustc helloworld.rs && du -h helloworld
235K helloworld
$ strip helloworld && du -h helloworld
133K helloworld
Небольшой вывод
Завершающим штрихом в нашем шаманстве могло быть удаление из исполняемого файла всей стандартной библиотеки. В большинстве случаев это не нужно, да и к тому же в офф.книге (или в переводе) все шаги подробно описаны. Этим способом можно получить файл размером, сопоставимым с аналогом на Си.
Стоит также отметить, что размер стандартного набора библиотек постоянен и сами линковочные файлы(перечисленные в статье) не увеличиваются в зависимости от вашего кода, а значит вам скорее всего не придется беспокоится о размерах. На крайний случай вы всегда можете использовать упаковщики кода вроде upx
Большое спасибо русскоязычному комьюнити Rust за помощь с переводом
Комментарии (72)
l4gfcm
11.07.2016 14:07+2В принципе это ещё не так плохо, как 10-ти мегабайтовый хелоуворлд на D. Причём экспорт отдельных функций резал только до 7-ми.
Хорошая идея != хорошая реализация.
Правда, времени, которое потратил на изучение, я не жалею, оно того стоило.bfDeveloper
11.07.2016 14:33+5Не надо бросаться камнями в D, не проверив на хоть сколько-то актуальной версии. На Win7 64-bit с DMD 2.070.0 helloworld занимает 200 килобайт. Без ключей, без конфигов. Если заморочиться размером, то можно не отставать от C. Вот, например, helloworld в 438 байт: http://forum.dlang.org/thread/qcbicxrtmjmwiljsyhdf@forum.dlang.org
l4gfcm
11.07.2016 16:16+1В винде ниразу не собирал, предполагалось что там всё ещё хуже. На самом деле, это вполне радужное открытие для меня. Но покуда приходится только наблюдать.
Буду знать почему он сейчас подымается в tiobe — есть нормальные реализации.bfDeveloper
11.07.2016 16:42+3Справедливости ради отмечу, что под линуксом GDC действительно сгенерировал 10 мегабайт. Но gdc сейчас во многом отстаёт, вон и версия языка только 2.066.
Свежий LDC сгенерировал 645 килобайт. Много, но я не пытался ничего ужимать. Думаю, что тут как и у Rust пожмётся выкидыванием лишнего.
Dicebot
11.07.2016 16:45+2Чтобы не было голословного обмена утверждениями, вот данные по актуальным компиляторам на x86_64 Arch Linux (
-O -inline
+strip
):
-rwxr-xr-x 1 dicebot dicebot 12K Jul 11 16:44 ./hello-dmd-shared -rwxr-xr-x 1 dicebot dicebot 558K Jul 11 16:43 ./hello-dmd-static -rwxr-xr-x 1 dicebot dicebot 1.8M Jul 11 16:44 ./hello-gdc-static -rwxr-xr-x 1 dicebot dicebot 12K Jul 11 16:44 ./hello-ldc-shared
(ldc-static и gdc-shared под рукой не было, но общая картина должна быть очевидна)
NeoCode
11.07.2016 17:48Кстати это может быть одним из тестов на «профпригодность» языка. Бинарник «hello world» должен быть меньше килобайта (и это с учетом всех таблиц, коих напихали в экешники всех форматов).
Какой язык сейчас может пройти этот тест? Разве что Ассемблер))0xd34df00d
12.07.2016 21:36+1Тест на профпригодность написания маленьких хелловорлдов?
Обычно важнее скорость роста размера бинарника.
amarao
11.07.2016 14:25+3Напоминает историю с delphi. Общеизвестно было, что «дельфя делает гигантские бинарники». Малоизвестно было, что можно было собирать приложения на Delphi без стандартных библиотек и получать исполняемые файлы в единицы килобайт.
У меня другой вопрос: а почему rust не использует динамически линкуемые библиотеки для подключения стандартной библиотеки?DrPass
11.07.2016 14:35+2> а почему rust не использует динамически линкуемые библиотеки для подключения стандартной библиотеки?
Ну это же не решает проблему размера бинарника. Разве что позволяет вам выбирать, таскать стандартную библиотеку вместе с вашим приложением, или разделять её между разными приложениями в системном хранилище, вместе с вероятностью получить dll hell.amarao
11.07.2016 14:43+2Опять началось. DLL hell давно отсутствует при правильно версионированных so'шках, rust может делать то же самое. Он никогда не станет системным языком, если не решит проблемы внешних библиотек.
Вариант «таскать всё своё с собой» вызывает серьёзнейшее сопротивление со стороны security team в дистрибутивах. Потому что если в очередном libssl найдут дыру размером в пол-интернета, то куда проще залатать одну libssl и перезапустить все приложения, чем пересобирать все приложения (судорожно пытаясь понять, какие из них от этой библиотеки зависят).DrPass
11.07.2016 15:22> DLL hell давно отсутствует при правильно версионированных so'шках
Вы же понимаете, что это как на дороге — каким бы аккуратным водителем вы бы не были, вы никогда не сможете поручиться за других участников дорожного движения.amarao
11.07.2016 15:28+3Да, вменяемость системы определяется вменяемостью каждого автора каждой используемой библиотеки. Обычно «вменяемость» происходящего модерируется мейнтейнерами дистрибутивов и всякими штуками вроде update-alternatives. Но это единственная работающая in-the-wild модель с системными зависимостями. Альтернатива — статическая или псевдостатическая (bring your own DLLs) компоновка, главная проблема которой — если всё переписывать на статику, никакой памяти не хватит.
snuk182
11.07.2016 18:38+21. Чем меньше в механизме сочленяющихся частей, тем он надежней. С либами плюс минус то же самое. Хотя реализация версионирования либ в линуксе мне нравится (с одним, правда, но — если либа стянута с неподтвержденного источника, плохо будет всем, кто ее использует).
2. Пока что память стоит дешевле труда по отлову багов, связанных с разными версиями либ. Пример Андроида это хорошо показывает.amarao
11.07.2016 18:49+1Вы знаете, я не соглашусь с этим пунктом. Чем меньше в механизме гибких сочленений, тем более катастрофически он ломается при первой невыносимой нагрузке. Любой, кто задевал зеркалами заднего вида зеркала соседа знает, какое это чудо — складывающиеся зеркала заднего вида. А могли бы и отламываться.
Память стоит дешевле до определённого предела. Если вам сейчас выкатить системные требования в 20Гб на телефон entry level, никому мало не покажется. И внутри андроида ровно те же so'шки, что и всюду, просто эти so'шки либо системные, либо замаскированы под «вызовы API play store».
Отказ от повторного использования кода — это индустриальная катастрофа на текущем уровне. И на любом я думаю, потому что удесятирение требований по памяти ко всему (а не только к Главному Бизнес-Приложению) — это слишком много.
До тех пор пока мы говорим, что Главное Бизнес-Приложение может прожить без линковки — ок, ок. Но это не индустриальный язык, это уютная песочница класса python/go/ruby/php/java. Никто не будет писать код начальной инициализации системы без разделяемых библиотек.ozkriff
11.07.2016 18:53+1Если ржавчина станет настолько популярна, что легкое распухание сотен тысяч исполняемых файлов на компьютере рядового пользователя станет серьезной проблемой, то тогда и будут стабилизировать ABI и все это разгребать. На данном этапе развития языка это далеко далеко не самая актуальная проблема.
amarao
11.07.2016 18:59-1окай. Но тогда не следует говорить о претензиях на «новый системный язык», увы. Конкурент Go — да. Конкурент Си — нет.
ozkriff
11.07.2016 19:02+2Все по разному понимают что такое "системный язык". Под одни определения ржавчина подходит, под другие — нет.
ozkriff
11.07.2016 19:22+4Это же не какой-то врожденный недостаток самого языка. Просто язык новый, он активно развивается, как и его основной компилятор, а стабилизация ABI усложнит эту работу. Плюс, на ржавчине в данный момент нет готовых и полезных широкой общественности приложений или библиотек.
Да и вообще, у ржавчины даже просто стандарта языка еще нет)
snuk182
11.07.2016 19:24+1> Конкурент Go — да. Конкурент Си — нет.
Здесь уже я не соглашусь — у Go порог вхождения сильно ниже. Как кто-то из буржуйнета метко заметил — «Go is a new PHP». Ну и при всем желании из Go рантайм на данном этапе развития не выковыряешь. Тем не менее я бы не сказал, что толстый бинарь останавливает народ от изучения Rust для написания низкоуровневых вещей. По своему опыту работы с Rust и гуглением проблем насущных, с ним связанных, могу заметить, что людям очень не хватает разжеванных примеров реализации шаблонов проектирования, к чему все привыкли за десятилетия ООП. Подвижки в этом плане есть, но уж очень вялые, ввиду непривычности управления памятью (фраза «borrow checker shortcomings» встречалась мне довольно часто). Так что я бы сказал, что претензии на «новый системный язык» обоснованы, но дорабатывать напильником надо дофига, причем в основном документацию.
defuz
11.07.2016 23:38+6В Rust можно создавать бинарники вообще без стандартной библиотеки. Хотите динамически линковаться с libc как это делает C – никаких проблем. Тоже самое с использованием стандартного аллокатора вместо «продвинутого» jemalloc.
Так что вполне себе конкурент и «новый системный язык».
snuk182
11.07.2016 19:29> Отказ от повторного использования кода — это индустриальная катастрофа на текущем уровне. И на любом я думаю, потому что удесятирение требований по памяти ко всему (а не только к Главному Бизнес-Приложению) — это слишком много.
Соглашусь наполовину. Многое зависит и от, так сказать, хостера библиотек — операционной системы. В дебиане и производных все реализуемо. В винде увы.
Если что, в андроиде я имел в виду джаву, а не си. С джавой ведь теоретически динамическую подгрузку тоже можно было бы реализовать, но для несистемных приложений ее решили не делать, и разработчиков здесь тоже можно понять.
Mingun
11.07.2016 19:41+7Правда почему-то латание дыры в одной libssl не спасает от того, что обновляются десятки пакетов, зависящих от нее. Хотя вроде как по концепции в них ничего не должно было поменяться. Я даже задавал по этому поводу вопрос на тостере, но внятного ответа так и не получил. Так что похоже не особо это всех и волнует (и меня в том числе)
l4l
11.07.2016 14:42+4Вообще есть такая штука "-C prefer-dynamic", если не ошибаюсь. Но тогда проблема — как эти библиотеки доставлять. Rust API не очень стабильно, поэтому, по крайней мере сейчас, имеет смысл все тащить с собой
amarao
11.07.2016 15:10+2О, а вот тут вот как раз интересное и вылезает. Написать клёвую штуку — это одно. Сделать его индустриальным решением — совсем другое. Стабильность API на уровне версий so'шек — как раз из этого.
Чтобы rust могу претендовать на замену Си, он должен уметь не меньше, чем Си для большинства случаев. Работа с системными библиотеками — как раз то, что хотят «для большинства случаев». А вот статическая линковка — это уже экзотика, нужная в редких случаях.
(И не надо мне говорить, что статическая линковка спасёт мир. Я сижу за ноутбуком с 4Гб памяти и мне этого за глаза и за уши, не смотря на то, что если сделать декартво произведение от запущенных бинарников и их so'шек, там получится за 16Гб только кода — столько в память без swap'а не засунуть).ozkriff
11.07.2016 17:50если сделать декартво произведение от запущенных бинарников и их so'шек
По идее, во время сборки неиспользуемые куски зависимостей будут выкинуты оптимизатором.
amarao
11.07.2016 18:41+1А если они используемые? Каждая гуёвая программа хочет себе «весь qt» или «весь gtk», который в свою очередь хочет всего и вся.
Мой эстимейт (посмотрел на нескольких программах) — удесятирение размера кода. Т.е. вместо 2Гб за глаза и за уши — «16Гб не достаточно».ozkriff
11.07.2016 19:08+1А если они используемые?
Тогда все плохо, конечно, и остается только разводить филосовские беседы по поводу тотального доминирования раздутого и переусложненного ПО в индустрии и с грустью смотреть на всякие экспериментальные минималистичные штуки в духе http://sta.li ))
mayorovp
11.07.2016 14:29+7Весь пост можно было сократить до простого "в Rust рантайм линкуется статически". Зачем так много букв?
vanxant
11.07.2016 14:36+3Вспомнилось из исходников винды 3.11
char make_program_look_big[10000000000]
timeshift
11.07.2016 15:21+1А в чем отличие jemalloc от стандартного аллокатора? И почему в Rust он используется чаще всего?
l4l
11.07.2016 15:32+5jemalloc is a general purpose malloc(3) implementation that emphasizes fragmentation avoidance and scalable concurrency support
Либо более наглядноИсточник: https://www.percona.com/blog/2013/03/08/mysql-performance-impact-of-memory-allocators-part-2/
Beholder
11.07.2016 15:55+2А можете что-нибудь рассказать про это:
Представлен новый формат исполняемых контейнеров (crate type) cdylib, который рассчитан на компиляцию динамических библиотек, предназначенных для встраивания в программы на других языках. Ранее доступный формат dylib позиционируется для создания динамических библиотек для проектов на языке Rust. В отличие от dylib в cdylib не включаются метаданные, экспортируемые символы аналогичны исполняемым файлам, не допускается LTO и все библиотеки связываются статически. Сборка простейшего примера "hello world" при использовании cdylib занимает 7.2 Кб, в то время как при dylib — 2.4 Мб;
(цитата с opennet.ru)
l4l
11.07.2016 16:52+1Могу порекомендовать взглянуть на соответствующий RFC
Beholder
11.07.2016 17:09Непонятно, можно ли это уже использовать в 1.10-stable (
rustc
не показывает вариант опции--crate-type=cdylib
) и как именно собрать "hello world" с таким размером.l4l
11.07.2016 17:26Очень странно, у меня все работает, что на stable, что на nightly
Кстати, значения кажутся очень преувеличенымиozkriff
11.07.2016 17:27А если с оптимизациями?
l4l
11.07.2016 17:35+2Да, оптимизация меняет дело:
strip
+opt-level=s
: 3.4кб против 1.7мбopt-level=s
: 4.1кб против 2.5мб
ozkriff
11.07.2016 17:43Есть у меня мысль, к сожалению, что тут оптимизатор все подчистую выкидывает просто, потому что ничего публичного не объявлено.
#[no_mangle] pub extern "C" fn hello() { println!("hello!"); }
это уже так хорошо не укукоживается.
Beholder
11.07.2016 18:46Это собираем крейт, а можно ли собрать с этим нововведением helloworld.exe минимального размера?
Занятно: сейчас при rustup update nightly касперский нашёл якобы троян в graphviz-c8005792.dll
ozkriff
11.07.2016 18:48а можно ли собрать с этим нововведением helloworld.exe минимального размера?
cdylib для библиотек нужен
рассчитан на компиляцию динамических библиотек, предназначенных для встраивания в программы на других языках
ozkriff
11.07.2016 17:37rustc не показывает вариант опции --crate-type=cdylib
У меня в справке варианта нет, но команда выполняется. Наверное, просто забыли обновить справку.
snuk182
11.07.2016 18:26Не вы первый :) users.rust-lang.org
ozkriff
11.07.2016 18:31Ну так статья потому и была написана, что вопрос очень часто всплывает у знакомящихся с языком. Чаще, наверное, чем медленная скорость работы кода (из-за отладочных сборок по умолчанию).
kstep
11.07.2016 18:27+6По поводу компиляций и оптимизаций, обновился только что до Rust 1.10.
Исходникиfn main() { println!("Hello, world!"); }
int main(int argc, char **argv) { printf("Hello, world!\n"); }
amarao
11.07.2016 18:43+1Во, вот это похоже на правду уже. Теперь вопрос в том, насколько в таком режиме можно свободно пользоваться rust'овым рантаймом. Не для hello world, а для всего-всего. И как много этого райнтайма будет вынесено как динамические библиотеки наружу.
И насколько получившийся бинарь будет способен пережить различия в минорных версиях компилятора (про мажорные пока ни слова) между программой и её библиотеками.ozkriff
11.07.2016 18:59+1насколько в таком режиме можно свободно пользоваться rust'овым рантаймом
А какие вообще сложности с этим могут быть? Тут только теряется автономность приложения.
И как много этого райнтайма будет вынесено как динамические библиотеки наружу.
Смотря что конкретно иметь ввиду под "рантайм", но в обычном смысле, наверное, можно сказать что он весь во внешней библиотеке.
насколько получившийся бинарь будет способен пережить различия в минорных версиях компилятора
Насколько я знаю, с этим никаких гарантий никто не дает сейчас.
Scf
12.07.2016 10:23+4У динамической линковки есть только один большой плюс — это экономия оперативной памяти, т.к. внешняя библиотека грузится один раз и затем маппится в каждый процесс. Ну и экономия диска, да. Но актуальном все это становится на современном железе, только если таких скомпилированных приложений и процессов — десятки.
А если делать выбор между "скопируй бинарник на другую машину и он заработает" и "без установленных библиотек правильной версии не запустится"...
mayorovp
12.07.2016 10:29+4У динамической линковки есть только один большой плюс — это экономия оперативной памяти, т.к. внешняя библиотека грузится один раз и затем маппится в каждый процесс.
Это только если релокации не произойдет.
Dicebot
12.07.2016 15:05+3Есть ещё несколько плюсов:
1) более эффективная работа с процессорным кэшем инструкций при переключении между разными процессами использующими одну и ту же библиотеку
2) меньше размер пакета для развёртывания на сервере (полезно в основном владельцам бюджетных VPS с лимитами на upload)
3) централизованное получение security обновлений если авторы библиотеки серьёзно относятся к версионирования ABI
Я бы сказал что динамическая линковка строго лучше статической, но только для стабильных библиотек с строгой системой версий. Что почти никогда не применимо к пакетам cargo и им подобным, но можно ожидать от стандартной библиотеки.
amdf
12.07.2016 10:25-1В чём преимущество бинарника 6 Кб вместо 600 Кб? В каких задачах сокращение объёма исполняемого файла даст существенное преимущество?
prospero78su
12.07.2016 12:00+2Экономия дискового пространства, сокращение времени чтения с диска?
amdf
12.07.2016 14:04-1На современном железе не очень актуально, разве нет?
ozkriff
12.07.2016 14:14+4Смотря для какого современного железа, смотря что за ось и все такое.
Если я пишу игру и она на мегабайт стала больше — да и не важно.
Но если есть какая-то новая железяка с линуксом для встраивания куда-то там, то на ней увеличение размера каждой из сотен (или тысяч?) мелких юниксовых утилит за счет теоретического переписывания их на ржавчину очень легко может вызвать лишние сложности.
prospero78su
13.07.2016 09:38Когда на сервер ломится пару десятков тысяч пользователей — лишним не будет.
NeoCode
Все это следствие того что до сих пор применяется древняя как язык Си технология статической линковки.
Отсюда же и невозможность хранения шаблонов С++ в библиотеках и необходимость использования заголовочных файлов.
Пора уже придумать формат библиотек, в котором код хранился бы в виде синтаксического дерева, и пересмотреть сам процесс линковки как таковой.
amarao
Динамическая линковка в текущем виде — это, скорее, очень специфичное для Си. Мы настолько привыкли, что Си — это стандарт для системного ПО, что привыкли считать, что *.so (*.dll) — это системные библиотеки. А они не системные, они всего лишь «от языка Си».
NeoCode
DLL это вроде как часть Windows, насчет so не знаю но тоже думаю что это скорее часть Linux чем принадлежность языка (формально в стандарте Си вроде бы нет никаких упоминаний о dll или so).
Massacre
Ага, а для DOS были оверлеи и различные подгружаемые на старте «DOS экстендеры» в виде exe. Это всё от ОС зависит.
NeoCode
Тут скорее претензии к формату *.lib / *.a — именно там что-то криво реализовано и в результате линкуется вся библиотека (и эти же форматы не приспособлены хранить AST различных шаблонов). Хотя в случае Rust возможно и к самому языку — зачем линковать «рантайм» статически и что это за рантайм вообще такой, если от программы хотят вывести строку на консоль?
ozkriff
Потому что он для этого нужен по умолчанию, если специально не извращаться. Просто у Си рантайм идет сразу со всеми распространенными ОСями.
https://www.rust-lang.org/en-US/faq.html#does-rust-have-a-runtime
NeoCode
Вопрос в том, зачем тащить ВЕСЬ рантайм, если нужна всего лишь ОДНА функция?
Почему рантайм монолитный, почему компилятор не умеет добавлять в экзешник только то, что нужно конкретно этому экзешнику?
ozkriff
Совсем весь рантайм и не тащится, но обычная печать косвенно задействует значительную его часть: если что-то не так пошло с печатью, то может потребоваться раскрутить стек, хоть он и не большой, да еще и напечатать бэктрейс, а это за собой тащит как минимум форматирование и аллокатор.
Я как-то так ситуацию себе представляю, хотя сам был бы рад статье со свежей и подробной информацией по теме, а то гуглятся, в основном, объяснения которым уже пара лет, когда еще libgreen был.
NeoCode
Ну вот, а все это — следствие того, что код в библиотеках хранится уже в окончательно скомпилированном виде, а не в виде заготовки — синтаксического дерева.
Если в конфигурации нашего проекта нет обработки исключений и аллокации памяти (допустим, железка с парой киболайт оперативки на борту), то код генерации исключений в библиотечных функциях должен при компиляции заменяться на просто return с кодом ошибки. Как-то так.
ozkriff
Разве? Мне кажется протаскивание в helloworld значительной части стандартной библиотеки является следствием параметров сборки по умолчанию и архитектуры стандартной библиотеки.
Так в helloworld выше они неявно есть, надо просто явно их отключить.
Ну и, кстати, Cargo пока только с зависимостями в виде исходников и умеет работать, так что при изменении параметров сборки из зависимостей как раз вполне могут быть выкинуты целые куски еще на уровне исходников. Не очень понимаю, что принципиально должна изменить инновация с ast-зависимостями, да и как вообще все это должно работать.
amarao
Именно. Формально нет, по факту — на этом всё держится. Динамическая линковка — стандартная практика индустриального Си.
ozkriff
По умолчанию ржавчина так и делает для своих собственных библиотек, иначе бы обобщенный код из библиотек был бы недоступен (заголовочных файлов-то нет).
ozkriff
Хм, вижу два минуса — где я не прав?
http://stackoverflow.com/questions/27999559/can-libraries-be-distributed-as-a-binary-so-the-end-user-cannot-see-the-source
billyevans
Кстати, как раз в С, при статической линковки компилятор(по крайней мере gcc) оторвет все лишнее. Если хочется всю либу засунуть в бинарь, мало кто, что захочет из .so-ки вызвать, то там особые флаги для этого нужны.