From: Алексей
Date: 2021-09-17 12:11:44Z
Проблема статической линковки в том, что она тащит в бинарник тонны неиспользуемого мусора даже для такого простого языка как C, где вроде как должно быть возможно выкинуть из бинарника весь тот код, который там реально не используется.
Так, если я создаю простейший Hello World на C с использованием SDL, который всего лишь выводит пустое окно, то при динамической линковке он будет весить только 3Кб, а если слинковать SDL статически - больше метра (почти полный размер данной библиотеки!).
Нет, я конечно понимаю, что там есть какой-то инициализационный код, но блин не 90% же кодовой базы! То есть линкер засунул в бинарник тонны балласта, который очевидно никогда и ни при каких условиях не выполняется.
И это всего лишь C. В случае C++ ситуация резко ухудшается. Попробуйте слинковать статически Qt-проект, даже самый простой, у вас там десятки Мб сразу выйдут, потому что из-за особенностей C++ не существует способа достоверно определить, будет данный код исполняться хоть когда-то или же нет.
Когда же у нас сам язык динамический по своей природе, типа C# или Java, тут и вовсе нормальный линкер сделать невозможно в принципе, и Dependency Hell неизбежен. Одно время даже была такая шутка: "Программы на Java по-настоящему кроссплатформенны, на любом железе НЕ работают абсолютно одинаково". Даже сейчас запустить какой-нибудь jar-файл - это минимум 15 мин трахотни с зависимостями, начиная с подбора правильной версии/сборки JRE.
From: Sergey Matveev
Date: 2021-09-17 13:29:13Z
*** Алексей [2021-09-17 15:11]:
>Проблема статической линковки в том, что она тащит в бинарник тонны неиспользуемого мусора
А это уже вопрос, в том числе, как собрана эта библиотека. Библиотеки
разбиваются на секции -- грубо говоря, секция ELF-а это неделимый атом.
Если в секции содержится foo() и bar() функции, а нам нужна только
foo(), то всё равно при линковке будет засунута вся секция, включая этот
bar(). Если указать -ffunction-sections, то каждая функция будет в своей
отдельной секции и это позволит не включать ничего что не используется.
Я не раз замечал что многие не выставляют даже этот простой аргумент и
поэтому их статические библиотеки при линковке могут быть огромными,
действительно.
>Так, если я создаю простейший Hello World на C с использованием SDL, который всего лишь выводит пустое окно, то при динамической линковке он будет весить только 3Кб
Вот только в оперативную память будет загружен всё равно мегабайт
динамической библиотеки. И даже если для SDL всё равно требуется 90% его
кода, то динамическая библиотека была бы tradeoff-ом между местом на
диске и дорогостящими CPU операциями по динамической линковке в
real-time, так сказать. Статический файл я просто загружаю и передаю ему
управление, а с динамической линковкой, я вон уже как-то показывал в
одном из комментариев к ImageMagick-у в своём блоге, что 300мс (!!!)
тратится просто на запуск convert утилиты и никак это время не
сократить, которое просто "подключает" динамические загруженные
библиотеки к новому загружаемому процессу. Уж извините, но нет, я готов
потратить лишний мегабайт хоть для каждого .exe-шника, лишь бы впустую
не тратить тьму времени, которое я даже визуально могу заметить, при
*каждом* запуске этого .exe.
Я думаю или тут SDL так устроен что он почти весь свой код использует и
поэтому он линкуется, или он как-то скомпонован криво или банально
собран без каких-нибудь -ffunction-sections. Если написать printf("hello
world"), то при статической линковке только printf будет включена (ну
что ей там нужно, по мелочам) в исполняемый файл, а не вся libc. При
динамической линковке, даже когда нам нужен только printf, то всё равно
полностью весь libc будет загружен в память. С libc то это не проблема,
ибо его все используют, а вот с остальными уже может стать проблемой.
Вообще-то именно как-раз возможность не включать неиспользуемые вещи и
есть основная фишка статической линковки. То что не все умеют "её
готовить" -- это другой вопрос.
>Когда же у нас сам язык динамический по своей природе, типа C# или Java, тут и вовсе нормальный линкер сделать невозможно в принципе
Потому что этот подход (всё на свете динамическое) -- просто не нужен,
создаёт сплошные проблемы. Вызывать сторонние функции можно и через RPC,
пускай даже ценой небольшой просадки по производительности. Можно
выдумывать много проблем для которых динамическая линковка бы была
нужна, а можно не создавать этих проблем и вовсе.