Disclaimer: Эта статья является очень вольным переводом и некоторые мометы достаточно сильно отличаются от оригинала

Бороздя просторы интернета вы наверняка уже успели услышать про 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)


  1. NeoCode
    11.07.2016 14:07
    +2

    Все это следствие того что до сих пор применяется древняя как язык Си технология статической линковки.
    Отсюда же и невозможность хранения шаблонов С++ в библиотеках и необходимость использования заголовочных файлов.
    Пора уже придумать формат библиотек, в котором код хранился бы в виде синтаксического дерева, и пересмотреть сам процесс линковки как таковой.


    1. amarao
      11.07.2016 14:27
      +1

      Динамическая линковка в текущем виде — это, скорее, очень специфичное для Си. Мы настолько привыкли, что Си — это стандарт для системного ПО, что привыкли считать, что *.so (*.dll) — это системные библиотеки. А они не системные, они всего лишь «от языка Си».


      1. NeoCode
        11.07.2016 17:44

        DLL это вроде как часть Windows, насчет so не знаю но тоже думаю что это скорее часть Linux чем принадлежность языка (формально в стандарте Си вроде бы нет никаких упоминаний о dll или so).


        1. Massacre
          11.07.2016 17:56

          Ага, а для DOS были оверлеи и различные подгружаемые на старте «DOS экстендеры» в виде exe. Это всё от ОС зависит.


          1. NeoCode
            11.07.2016 18:01

            Тут скорее претензии к формату *.lib / *.a — именно там что-то криво реализовано и в результате линкуется вся библиотека (и эти же форматы не приспособлены хранить AST различных шаблонов). Хотя в случае Rust возможно и к самому языку — зачем линковать «рантайм» статически и что это за рантайм вообще такой, если от программы хотят вывести строку на консоль?


            1. ozkriff
              11.07.2016 18:25
              +1

              Хотя в случае Rust возможно и к самому языку — зачем линковать «рантайм» статически ..., если от программы хотят вывести строку на консоль?

              Потому что он для этого нужен по умолчанию, если специально не извращаться. Просто у Си рантайм идет сразу со всеми распространенными ОСями.


              … и что это за рантайм вообще такой ...

              https://www.rust-lang.org/en-US/faq.html#does-rust-have-a-runtime


              1. NeoCode
                12.07.2016 09:52
                +1

                Вопрос в том, зачем тащить ВЕСЬ рантайм, если нужна всего лишь ОДНА функция?
                Почему рантайм монолитный, почему компилятор не умеет добавлять в экзешник только то, что нужно конкретно этому экзешнику?


                1. ozkriff
                  12.07.2016 10:23
                  +2

                  Совсем весь рантайм и не тащится, но обычная печать косвенно задействует значительную его часть: если что-то не так пошло с печатью, то может потребоваться раскрутить стек, хоть он и не большой, да еще и напечатать бэктрейс, а это за собой тащит как минимум форматирование и аллокатор.


                  Я как-то так ситуацию себе представляю, хотя сам был бы рад статье со свежей и подробной информацией по теме, а то гуглятся, в основном, объяснения которым уже пара лет, когда еще libgreen был.


                  1. NeoCode
                    12.07.2016 19:20

                    Ну вот, а все это — следствие того, что код в библиотеках хранится уже в окончательно скомпилированном виде, а не в виде заготовки — синтаксического дерева.
                    Если в конфигурации нашего проекта нет обработки исключений и аллокации памяти (допустим, железка с парой киболайт оперативки на борту), то код генерации исключений в библиотечных функциях должен при компиляции заменяться на просто return с кодом ошибки. Как-то так.


                    1. ozkriff
                      13.07.2016 10:05
                      +1

                      все это — следствие того, что код в библиотеках хранится уже в окончательно скомпилированном виде

                      Разве? Мне кажется протаскивание в helloworld значительной части стандартной библиотеки является следствием параметров сборки по умолчанию и архитектуры стандартной библиотеки.


                      Если в конфигурации нашего проекта нет обработки исключений и аллокации памяти

                      Так в helloworld выше они неявно есть, надо просто явно их отключить.


                      Ну и, кстати, Cargo пока только с зависимостями в виде исходников и умеет работать, так что при изменении параметров сборки из зависимостей как раз вполне могут быть выкинуты целые куски еще на уровне исходников. Не очень понимаю, что принципиально должна изменить инновация с ast-зависимостями, да и как вообще все это должно работать.


        1. amarao
          11.07.2016 18:33

          Именно. Формально нет, по факту — на этом всё держится. Динамическая линковка — стандартная практика индустриального Си.


    1. ozkriff
      11.07.2016 18:15
      +2

      Пора уже придумать формат библиотек, в котором код хранился бы в виде синтаксического дерева, и пересмотреть сам процесс линковки как таковой.

      По умолчанию ржавчина так и делает для своих собственных библиотек, иначе бы обобщенный код из библиотек был бы недоступен (заголовочных файлов-то нет).



    1. billyevans
      11.07.2016 18:49
      +1

      Кстати, как раз в С, при статической линковки компилятор(по крайней мере gcc) оторвет все лишнее. Если хочется всю либу засунуть в бинарь, мало кто, что захочет из .so-ки вызвать, то там особые флаги для этого нужны.


  1. l4gfcm
    11.07.2016 14:07
    +2

    В принципе это ещё не так плохо, как 10-ти мегабайтовый хелоуворлд на D. Причём экспорт отдельных функций резал только до 7-ми.
    Хорошая идея != хорошая реализация.
    Правда, времени, которое потратил на изучение, я не жалею, оно того стоило.


    1. 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


      1. l4gfcm
        11.07.2016 16:16
        +1

        В винде ниразу не собирал, предполагалось что там всё ещё хуже. На самом деле, это вполне радужное открытие для меня. Но покуда приходится только наблюдать.
        Буду знать почему он сейчас подымается в tiobe — есть нормальные реализации.


        1. bfDeveloper
          11.07.2016 16:42
          +3

          Справедливости ради отмечу, что под линуксом GDC действительно сгенерировал 10 мегабайт. Но gdc сейчас во многом отстаёт, вон и версия языка только 2.066.
          Свежий LDC сгенерировал 645 килобайт. Много, но я не пытался ничего ужимать. Думаю, что тут как и у Rust пожмётся выкидыванием лишнего.


    1. phantom-code
      11.07.2016 14:37

      Сейчас Hello world на D занимает около мегабайта. Никак не 10.


    1. 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 под рукой не было, но общая картина должна быть очевидна)


    1. NeoCode
      11.07.2016 17:48

      Кстати это может быть одним из тестов на «профпригодность» языка. Бинарник «hello world» должен быть меньше килобайта (и это с учетом всех таблиц, коих напихали в экешники всех форматов).
      Какой язык сейчас может пройти этот тест? Разве что Ассемблер))


      1. 0xd34df00d
        12.07.2016 21:36
        +1

        Тест на профпригодность написания маленьких хелловорлдов?

        Обычно важнее скорость роста размера бинарника.


  1. amarao
    11.07.2016 14:25
    +3

    Напоминает историю с delphi. Общеизвестно было, что «дельфя делает гигантские бинарники». Малоизвестно было, что можно было собирать приложения на Delphi без стандартных библиотек и получать исполняемые файлы в единицы килобайт.

    У меня другой вопрос: а почему rust не использует динамически линкуемые библиотеки для подключения стандартной библиотеки?


    1. DrPass
      11.07.2016 14:35
      +2

      > а почему rust не использует динамически линкуемые библиотеки для подключения стандартной библиотеки?
      Ну это же не решает проблему размера бинарника. Разве что позволяет вам выбирать, таскать стандартную библиотеку вместе с вашим приложением, или разделять её между разными приложениями в системном хранилище, вместе с вероятностью получить dll hell.


      1. amarao
        11.07.2016 14:43
        +2

        Опять началось. DLL hell давно отсутствует при правильно версионированных so'шках, rust может делать то же самое. Он никогда не станет системным языком, если не решит проблемы внешних библиотек.

        Вариант «таскать всё своё с собой» вызывает серьёзнейшее сопротивление со стороны security team в дистрибутивах. Потому что если в очередном libssl найдут дыру размером в пол-интернета, то куда проще залатать одну libssl и перезапустить все приложения, чем пересобирать все приложения (судорожно пытаясь понять, какие из них от этой библиотеки зависят).


        1. DrPass
          11.07.2016 15:22

          > DLL hell давно отсутствует при правильно версионированных so'шках
          Вы же понимаете, что это как на дороге — каким бы аккуратным водителем вы бы не были, вы никогда не сможете поручиться за других участников дорожного движения.


          1. amarao
            11.07.2016 15:28
            +3

            Да, вменяемость системы определяется вменяемостью каждого автора каждой используемой библиотеки. Обычно «вменяемость» происходящего модерируется мейнтейнерами дистрибутивов и всякими штуками вроде update-alternatives. Но это единственная работающая in-the-wild модель с системными зависимостями. Альтернатива — статическая или псевдостатическая (bring your own DLLs) компоновка, главная проблема которой — если всё переписывать на статику, никакой памяти не хватит.


            1. snuk182
              11.07.2016 18:38
              +2

              1. Чем меньше в механизме сочленяющихся частей, тем он надежней. С либами плюс минус то же самое. Хотя реализация версионирования либ в линуксе мне нравится (с одним, правда, но — если либа стянута с неподтвержденного источника, плохо будет всем, кто ее использует).
              2. Пока что память стоит дешевле труда по отлову багов, связанных с разными версиями либ. Пример Андроида это хорошо показывает.


              1. amarao
                11.07.2016 18:49
                +1

                Вы знаете, я не соглашусь с этим пунктом. Чем меньше в механизме гибких сочленений, тем более катастрофически он ломается при первой невыносимой нагрузке. Любой, кто задевал зеркалами заднего вида зеркала соседа знает, какое это чудо — складывающиеся зеркала заднего вида. А могли бы и отламываться.

                Память стоит дешевле до определённого предела. Если вам сейчас выкатить системные требования в 20Гб на телефон entry level, никому мало не покажется. И внутри андроида ровно те же so'шки, что и всюду, просто эти so'шки либо системные, либо замаскированы под «вызовы API play store».

                Отказ от повторного использования кода — это индустриальная катастрофа на текущем уровне. И на любом я думаю, потому что удесятирение требований по памяти ко всему (а не только к Главному Бизнес-Приложению) — это слишком много.

                До тех пор пока мы говорим, что Главное Бизнес-Приложение может прожить без линковки — ок, ок. Но это не индустриальный язык, это уютная песочница класса python/go/ruby/php/java. Никто не будет писать код начальной инициализации системы без разделяемых библиотек.


                1. ozkriff
                  11.07.2016 18:53
                  +1

                  Если ржавчина станет настолько популярна, что легкое распухание сотен тысяч исполняемых файлов на компьютере рядового пользователя станет серьезной проблемой, то тогда и будут стабилизировать ABI и все это разгребать. На данном этапе развития языка это далеко далеко не самая актуальная проблема.


                  1. amarao
                    11.07.2016 18:59
                    -1

                    окай. Но тогда не следует говорить о претензиях на «новый системный язык», увы. Конкурент Go — да. Конкурент Си — нет.


                    1. ozkriff
                      11.07.2016 19:02
                      +2

                      Все по разному понимают что такое "системный язык". Под одни определения ржавчина подходит, под другие — нет.


                    1. ozkriff
                      11.07.2016 19:22
                      +4

                      Это же не какой-то врожденный недостаток самого языка. Просто язык новый, он активно развивается, как и его основной компилятор, а стабилизация ABI усложнит эту работу. Плюс, на ржавчине в данный момент нет готовых и полезных широкой общественности приложений или библиотек.


                      Да и вообще, у ржавчины даже просто стандарта языка еще нет)


                    1. snuk182
                      11.07.2016 19:24
                      +1

                      > Конкурент Go — да. Конкурент Си — нет.
                      Здесь уже я не соглашусь — у Go порог вхождения сильно ниже. Как кто-то из буржуйнета метко заметил — «Go is a new PHP». Ну и при всем желании из Go рантайм на данном этапе развития не выковыряешь. Тем не менее я бы не сказал, что толстый бинарь останавливает народ от изучения Rust для написания низкоуровневых вещей. По своему опыту работы с Rust и гуглением проблем насущных, с ним связанных, могу заметить, что людям очень не хватает разжеванных примеров реализации шаблонов проектирования, к чему все привыкли за десятилетия ООП. Подвижки в этом плане есть, но уж очень вялые, ввиду непривычности управления памятью (фраза «borrow checker shortcomings» встречалась мне довольно часто). Так что я бы сказал, что претензии на «новый системный язык» обоснованы, но дорабатывать напильником надо дофига, причем в основном документацию.


                    1. defuz
                      11.07.2016 23:38
                      +6

                      В Rust можно создавать бинарники вообще без стандартной библиотеки. Хотите динамически линковаться с libc как это делает C – никаких проблем. Тоже самое с использованием стандартного аллокатора вместо «продвинутого» jemalloc.

                      Так что вполне себе конкурент и «новый системный язык».


                1. snuk182
                  11.07.2016 19:29

                  > Отказ от повторного использования кода — это индустриальная катастрофа на текущем уровне. И на любом я думаю, потому что удесятирение требований по памяти ко всему (а не только к Главному Бизнес-Приложению) — это слишком много.
                  Соглашусь наполовину. Многое зависит и от, так сказать, хостера библиотек — операционной системы. В дебиане и производных все реализуемо. В винде увы.
                  Если что, в андроиде я имел в виду джаву, а не си. С джавой ведь теоретически динамическую подгрузку тоже можно было бы реализовать, но для несистемных приложений ее решили не делать, и разработчиков здесь тоже можно понять.


        1. Mingun
          11.07.2016 19:41
          +7

          Правда почему-то латание дыры в одной libssl не спасает от того, что обновляются десятки пакетов, зависящих от нее. Хотя вроде как по концепции в них ничего не должно было поменяться. Я даже задавал по этому поводу вопрос на тостере, но внятного ответа так и не получил. Так что похоже не особо это всех и волнует (и меня в том числе)


    1. l4l
      11.07.2016 14:42
      +4

      Вообще есть такая штука "-C prefer-dynamic", если не ошибаюсь. Но тогда проблема — как эти библиотеки доставлять. Rust API не очень стабильно, поэтому, по крайней мере сейчас, имеет смысл все тащить с собой


      1. amarao
        11.07.2016 15:10
        +2

        О, а вот тут вот как раз интересное и вылезает. Написать клёвую штуку — это одно. Сделать его индустриальным решением — совсем другое. Стабильность API на уровне версий so'шек — как раз из этого.

        Чтобы rust могу претендовать на замену Си, он должен уметь не меньше, чем Си для большинства случаев. Работа с системными библиотеками — как раз то, что хотят «для большинства случаев». А вот статическая линковка — это уже экзотика, нужная в редких случаях.

        (И не надо мне говорить, что статическая линковка спасёт мир. Я сижу за ноутбуком с 4Гб памяти и мне этого за глаза и за уши, не смотря на то, что если сделать декартво произведение от запущенных бинарников и их so'шек, там получится за 16Гб только кода — столько в память без swap'а не засунуть).


        1. ozkriff
          11.07.2016 17:50

          если сделать декартво произведение от запущенных бинарников и их so'шек

          По идее, во время сборки неиспользуемые куски зависимостей будут выкинуты оптимизатором.


          1. amarao
            11.07.2016 18:41
            +1

            А если они используемые? Каждая гуёвая программа хочет себе «весь qt» или «весь gtk», который в свою очередь хочет всего и вся.
            Мой эстимейт (посмотрел на нескольких программах) — удесятирение размера кода. Т.е. вместо 2Гб за глаза и за уши — «16Гб не достаточно».


            1. ozkriff
              11.07.2016 19:08
              +1

              А если они используемые?

              Тогда все плохо, конечно, и остается только разводить филосовские беседы по поводу тотального доминирования раздутого и переусложненного ПО в индустрии и с грустью смотреть на всякие экспериментальные минималистичные штуки в духе http://sta.li ))


  1. mayorovp
    11.07.2016 14:29
    +7

    Весь пост можно было сократить до простого "в Rust рантайм линкуется статически". Зачем так много букв?


    1. l4l
      11.07.2016 14:37
      +9

      Как минимум рассмотрено то, что в этом рантайме есть, а также то, как от этого можно избавиться


      1. asdf87
        12.07.2016 17:30
        -1

        Поддерживаю, кроме того далеко не все вообще знают, что у таких языков как С++ и Rust вообще есть рантайм


  1. vanxant
    11.07.2016 14:36
    +3

    Вспомнилось из исходников винды 3.11

    char make_program_look_big[10000000000]
    


  1. timeshift
    11.07.2016 15:21
    +1

    А в чем отличие jemalloc от стандартного аллокатора? И почему в Rust он используется чаще всего?


    1. l4l
      11.07.2016 15:32
      +5

      С оф.сайта:

      jemalloc 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/
      image


  1. 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)


    1. l4l
      11.07.2016 16:52
      +1

      Могу порекомендовать взглянуть на соответствующий RFC


      1. Beholder
        11.07.2016 17:09

        Непонятно, можно ли это уже использовать в 1.10-stable (rustc не показывает вариант опции --crate-type=cdylib) и как именно собрать "hello world" с таким размером.


        1. l4l
          11.07.2016 17:26

          Очень странно, у меня все работает, что на stable, что на nightly

          Кстати, значения кажутся очень преувеличеными
          image


          1. ozkriff
            11.07.2016 17:27

            А если с оптимизациями?


            1. l4l
              11.07.2016 17:35
              +2

              Да, оптимизация меняет дело:


              • strip + opt-level=s: 3.4кб против 1.7мб
              • opt-level=s: 4.1кб против 2.5мб


              1. ozkriff
                11.07.2016 17:43

                Есть у меня мысль, к сожалению, что тут оптимизатор все подчистую выкидывает просто, потому что ничего публичного не объявлено.


                #[no_mangle]
                pub extern "C" fn hello() {
                        println!("hello!");
                }

                это уже так хорошо не укукоживается.


          1. Beholder
            11.07.2016 18:46

            Это собираем крейт, а можно ли собрать с этим нововведением helloworld.exe минимального размера?


            Занятно: сейчас при rustup update nightly касперский нашёл якобы троян в graphviz-c8005792.dll


            1. ozkriff
              11.07.2016 18:48

              а можно ли собрать с этим нововведением helloworld.exe минимального размера?

              cdylib для библиотек нужен


              рассчитан на компиляцию динамических библиотек, предназначенных для встраивания в программы на других языках


        1. ozkriff
          11.07.2016 17:37

          rustc не показывает вариант опции --crate-type=cdylib

          У меня в справке варианта нет, но команда выполняется. Наверное, просто забыли обновить справку.


  1. snuk182
    11.07.2016 18:26

    Не вы первый :) users.rust-lang.org


    1. ozkriff
      11.07.2016 18:31

      Ну так статья потому и была написана, что вопрос очень часто всплывает у знакомящихся с языком. Чаще, наверное, чем медленная скорость работы кода (из-за отладочных сборок по умолчанию).


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


    1. amarao
      11.07.2016 18:43
      +1

      Во, вот это похоже на правду уже. Теперь вопрос в том, насколько в таком режиме можно свободно пользоваться rust'овым рантаймом. Не для hello world, а для всего-всего. И как много этого райнтайма будет вынесено как динамические библиотеки наружу.

      И насколько получившийся бинарь будет способен пережить различия в минорных версиях компилятора (про мажорные пока ни слова) между программой и её библиотеками.


      1. ozkriff
        11.07.2016 18:59
        +1

        насколько в таком режиме можно свободно пользоваться rust'овым рантаймом

        А какие вообще сложности с этим могут быть? Тут только теряется автономность приложения.


        И как много этого райнтайма будет вынесено как динамические библиотеки наружу.

        Смотря что конкретно иметь ввиду под "рантайм", но в обычном смысле, наверное, можно сказать что он весь во внешней библиотеке.


        насколько получившийся бинарь будет способен пережить различия в минорных версиях компилятора

        Насколько я знаю, с этим никаких гарантий никто не дает сейчас.


  1. Scf
    12.07.2016 10:23
    +4

    У динамической линковки есть только один большой плюс — это экономия оперативной памяти, т.к. внешняя библиотека грузится один раз и затем маппится в каждый процесс. Ну и экономия диска, да. Но актуальном все это становится на современном железе, только если таких скомпилированных приложений и процессов — десятки.


    А если делать выбор между "скопируй бинарник на другую машину и он заработает" и "без установленных библиотек правильной версии не запустится"...


    1. mayorovp
      12.07.2016 10:29
      +4

      У динамической линковки есть только один большой плюс — это экономия оперативной памяти, т.к. внешняя библиотека грузится один раз и затем маппится в каждый процесс.

      Это только если релокации не произойдет.


    1. Dicebot
      12.07.2016 15:05
      +3

      Есть ещё несколько плюсов:


      1) более эффективная работа с процессорным кэшем инструкций при переключении между разными процессами использующими одну и ту же библиотеку
      2) меньше размер пакета для развёртывания на сервере (полезно в основном владельцам бюджетных VPS с лимитами на upload)
      3) централизованное получение security обновлений если авторы библиотеки серьёзно относятся к версионирования ABI


      Я бы сказал что динамическая линковка строго лучше статической, но только для стабильных библиотек с строгой системой версий. Что почти никогда не применимо к пакетам cargo и им подобным, но можно ожидать от стандартной библиотеки.


      1. mayorovp
        12.07.2016 19:21
        +2

        К первому плюсу, опять-таки, применимо замечание про релокацию.


  1. amdf
    12.07.2016 10:25
    -1

    В чём преимущество бинарника 6 Кб вместо 600 Кб? В каких задачах сокращение объёма исполняемого файла даст существенное преимущество?


    1. prospero78su
      12.07.2016 12:00
      +2

      Экономия дискового пространства, сокращение времени чтения с диска?


      1. amdf
        12.07.2016 14:04
        -1

        На современном железе не очень актуально, разве нет?


        1. ozkriff
          12.07.2016 14:14
          +4

          Смотря для какого современного железа, смотря что за ось и все такое.


          Если я пишу игру и она на мегабайт стала больше — да и не важно.


          Но если есть какая-то новая железяка с линуксом для встраивания куда-то там, то на ней увеличение размера каждой из сотен (или тысяч?) мелких юниксовых утилит за счет теоретического переписывания их на ржавчину очень легко может вызвать лишние сложности.


        1. prospero78su
          13.07.2016 09:38

          Когда на сервер ломится пару десятков тысяч пользователей — лишним не будет.