Статическое связывание против динамического связывания

В настоящее время вы можете использовать библиотеку: https://github.com/deano2390/FlowTextView . Например:



        

374
задан Tim 11 January 2017 в 20:22
поделиться

15 ответов

  • Динамическое связывание может снизить общее потребление ресурсов (если более одного процесса совместно используют одну и ту же библиотеку (включая версию в «одной и той же», конечно)). Я считаю, что именно этот аргумент определяет его присутствие в большинстве сред. Здесь «ресурсы» включают дисковое пространство, оперативную память и кэш-память. Конечно, если ваш динамический компоновщик недостаточно гибок, существует риск DLL ад .
  • Динамическое связывание означает, что исправления ошибок и обновления до библиотек распространяются , чтобы улучшить ваш продукт , не требуя от вас что-либо отправлять.
  • Плагины всегда требуют динамического связывания.
  • Статическое связывание означает, что вы можете знать, что код будет работать в очень ограниченных средах (в начале процесса загрузки или в режиме восстановления).
  • Статическое связывание может сделать двоичные файлы более легкими для распространения в различных пользовательских средах (за счет отправки более крупной и более ресурсоемкой программы).
  • Статическое связывание может позволить немного ускорить запуск раз, но это зависит в некоторой степени как от размера, так и от сложности вашей программы и от деталей стратегия загрузки ОС.

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


Для решения проблем производительности и эффективности: это зависит от . ​​

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

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

Другая проблема: время загрузки. Вы оплачиваете расходы на погрузку в какой-то момент. Когда вы платите, эта стоимость зависит от того, как работает ОС, а также от того, какие ссылки вы используете. Возможно, вы бы предпочли отложить оплату, пока не узнаете, что вам это нужно.

Обратите внимание, что статическое-динамическое связывание традиционно не является проблемой оптимизации, поскольку обе они включают отдельную компиляцию вплоть до объектных файлов. Однако это не требуется: компилятор может в принципе «скомпилировать» «статические библиотеки» вначале в переваренную форму AST и «связать» их, добавив эти AST к тем, которые сгенерированы для основного кода, что расширяет возможности глобальной оптимизации. Ни одна из систем, которые я использую, не делает этого, поэтому я не могу комментировать, насколько хорошо это работает.

Способ ответить на вопросы о производительности всегда всегда путем тестирования (и использовать среду тестирования настолько, насколько это возможно, среду развертывания).

330
ответ дан Carolus 11 January 2017 в 20:22
поделиться

Статическое связывание включает файлы, которые нужны программе, в одном исполняемом файле.

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

(DLL = динамическая ссылка библиотека)

Динамически связанные исполняемые файлы компилируются быстрее и не так ресурсоемки.

1
ответ дан Nykal 11 January 2017 в 20:22
поделиться

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

3
ответ дан Govardhan Murali 11 January 2017 в 20:22
поделиться

В Unix-подобных системах динамическое связывание может усложнить жизнь «root» для использования приложения с общими библиотеками, установленными в труднодоступных местах. Это связано с тем, что динамический компоновщик обычно не обращает внимания на LD_LIBRARY_PATH или его эквивалент для процессов с привилегиями root. Иногда статическое связывание спасает день.

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

10
ответ дан Jonathan Leffler 11 January 2017 в 20:22
поделиться

Динамическое связывание требует дополнительного времени для ОС, чтобы найти динамическую библиотеку и загрузить ее. Со статической связью все вместе, и это однократная загрузка в память.

Также см. DLL Hell . Это сценарий, в котором загружаемая ОС не является той DLL, которая поставляется с вашим приложением, или той версией, которую ожидает ваше приложение.

7
ответ дан Thomas Matthews 11 January 2017 в 20:22
поделиться

Еще одна проблема, которая еще не обсуждалась, - это исправление ошибок в библиотеке.

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

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

5
ответ дан R Samuel Klatchko 11 January 2017 в 20:22
поделиться

Лучший пример динамического связывания - когда библиотека зависит от используемого оборудования. В древние времена математическая библиотека C считалась динамичной, поэтому каждая платформа могла использовать все возможности процессора для ее оптимизации.

Еще лучшим примером может быть OpenGL. OpenGl - это API, который по-разному реализован AMD и NVidia. И вы не можете использовать реализацию NVidia на карте AMD, потому что аппаратное обеспечение отличается. Из-за этого вы не можете статически связать OpenGL с вашей программой. Динамическое связывание используется здесь, чтобы позволить API быть оптимизированным для всех платформ.

7
ответ дан Arne 11 January 2017 в 20:22
поделиться

В этом подробно обсуждаются общие библиотеки на linux и влияние на производительность.

20
ответ дан Jeremy Friesner 11 January 2017 в 20:22
поделиться

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

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

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

32
ответ дан Smi 11 January 2017 в 20:22
поделиться

Это довольно просто, правда. Когда вы вносите изменения в свой исходный код, вы хотите подождать 10 минут для его сборки или 20 секунд? Двадцать секунд - это все, что я могу вынести. Кроме того, я либо достаю меч, либо начинаю думать о том, как я могу использовать отдельную компиляцию и связывание, чтобы вернуть его в зону комфорта.

11
ответ дан Hans Passant 11 January 2017 в 20:22
поделиться

Динамическое связывание является единственным практическим способом удовлетворения некоторых лицензионных требований, таких как LGPL .

64
ответ дан Mark Ransom 11 January 2017 в 20:22
поделиться

1 / Я принимал участие в проектах, в которых динамическое связывание и статическое связывание были сравнены, и разница не была определена достаточно маленькой, чтобы перейти на динамическое связывание (я не был частью теста, я просто знаю вывод)

2 / Динамическое связывание часто ассоциируется с PIC (позиционно-независимый код, код, который не нужно изменять в зависимости от адреса, по которому он загружен). В зависимости от архитектуры PIC может привести к другому замедлению, но это необходимо для того, чтобы получить выгоду от совместного использования динамически связанной библиотеки между двумя исполняемыми файлами (и даже двумя процессами одного исполняемого файла, если ОС использует рандомизацию адреса загрузки в качестве меры безопасности). Я не уверен, что все ОС позволяют разделить две концепции, но Solaris и Linux делают и ISTR, что HP-UX делает также.

3 / Я был в других проектах, которые использовали динамическое связывание для функции «простого патча». Но этот «легкий патч» делает распространение небольших исправлений немного проще и сложнее, чем кошмар версий. Нам часто приходилось настаивать на всем, а также отслеживать проблемы на сайте клиента, потому что была указана неправильная версия.

Я пришел к выводу, что я использовал исключение статического связывания:

  • для таких вещей, как плагины, которые зависят от динамического связывания

  • при совместном использовании важно (большие библиотеки, используемые несколькими процессами одновременно, такие как среда выполнения C / C ++, библиотеки GUI, ... которые часто управляются независимо и для которых строго определен ABI)

Если кто-то хочет использовать «легкий патч», я бы сказал, что библиотеки должны управляться так же, как большие библиотеки, описанные выше: они должны быть почти независимыми с определенным ABI, который не должен быть изменен исправлениями.

21
ответ дан AProgrammer 11 January 2017 в 20:22
поделиться

Я согласен с замечаниями dnmckee, а также:

  • Статически связанные приложения могут быть проще в развертывании, поскольку существует меньше или нет дополнительных зависимостей файлов (.dll / .so), которые могут вызвать проблемы, когда они отсутствуют или установлены не в том месте.
43
ответ дан stakx 11 January 2017 в 20:22
поделиться

1) основано на том факте, что для вызова функции DLL всегда используется дополнительный косвенный переход. Сегодня это обычно незначительно. Внутри DLL есть некоторые дополнительные затраты на процессоры i386, потому что они не могут генерировать независимый от позиции код. На amd64 переходы могут быть относительно счетчика программы, так что это огромное улучшение.

2) Это правильно. Оптимизация, основанная на профилировании, обычно дает 10-15% производительности. Теперь, когда скорость процессора достигла своего предела, возможно, стоит сделать это.

Я бы добавил: (3) компоновщик может упорядочить функции в более эффективную кэш-группировку, чтобы минимизировать дорогостоящие пропуски уровня кеша. Это также может особенно повлиять на время запуска приложений (основываясь на результатах, которые я видел с компилятором Sun C ++)

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

По этим причинам, если нет реальной необходимости в DLL, просто используйте статическую компиляцию.

РЕДАКТИРОВАТЬ (чтобы ответить на комментарий, подчеркивание пользователя)

Вот хороший ресурс о проблеме кода, независимого от позиции http://eli.thegreenplace.net/2011/11/03/ position-independent-code-pic-in-shared-library /

Как объяснено, x86 не имеет их AFAIK ни для чего другого, кроме 15-битных диапазонов перехода, а не для безусловных переходов и вызовов. Вот почему функции (от генераторов), имеющие более 32K, всегда были проблемой и нуждались во встроенных батутах.

Но в популярных x86 ОС, таких как Linux, вам не нужно заботиться о том, чтобы файл SO / DLL не генерировался с помощью переключателя gcc -fpic (который обеспечивает использование таблиц косвенных переходов). Потому что, если вы этого не сделаете, код будет просто исправлен, как обычный компоновщик переместит его. Но при этом он делает сегмент кода недоступным для совместного использования, и ему потребуется полное отображение кода с диска в память и касание всего этого, прежде чем его можно будет использовать (очистка большей части кэшей, обращение к TLB) и т. Д. Было время когда это считалось медленным ... слишком медленным.

Так что вы больше не получите никакой пользы.

Я не помню, какие ОС (Solaris или FreeBSD) доставляли мне проблемы с моей системой сборки Unix, потому что я просто не делал этого и задавался вопросом, почему она вылетала, пока я не применил -fPIC к gcc.

65
ответ дан Lothar 11 January 2017 в 20:22
поделиться

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

Я имею в виду то, что часто называют «встроенными системами», многие из которых в настоящее время все чаще используют операционные системы общего назначения, и эти системы используются для всего, что только можно себе представить.

Чрезвычайно распространенным примером являются устройства, использующие системы GNU / Linux, использующие Busybox . Я довел это до крайности с NetBSD , создав загрузочный образ системы i386 (32-разрядный), который включает в себя как ядро, так и его корневую файловую систему, причем последний содержит единственную статически-связанную (by crunchgen) двоичный файл с жесткими ссылками на все программы, который сам содержит все (по крайней мере, последний счет 274) стандартных полнофункциональных системных программ (большинство, кроме набора инструментов), и он меньше 20 мегабайт байтов (и, вероятно, работает очень удобно в системе с 64 МБ памяти (даже с несжатой корневой файловой системой и полностью в ОЗУ), хотя я не смог найти такой маленький для тестирования далее).

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

1114 Однако это еще не вся история. Я также обычно собираю и использую установки операционной системы NetBSD для своих систем полной разработки, статически связывая все двоичные файлы. Несмотря на то, что для этого требуется значительно больше дискового пространства (всего ~ 6,6 ГБ для x86_64 со всем, включая набор инструментов и статическую привязку X11) (особенно если одна таблица полных символов отладки доступна для всех программ, другая ~ 2,5 ГБ), результат все равно остается в целом работает быстрее, а для некоторых задач даже использует меньше памяти, чем обычная система с динамической связью, которая предназначена для совместного использования кодовых страниц библиотеки. Диск дешев (даже быстрый диск), и память для кеширования часто используемых файлов на диске также относительно дешева, но циклы ЦП на самом деле не такие, и они платят ld.so стоимость запуска каждого процесса, который запускается каждый раз. его запуск займет часы и часы циклов ЦП от задач, требующих запуска многих процессов, особенно когда одни и те же программы используются снова и снова, например, компиляторы в системе разработки. Связанные со статическими данными программы могут сократить время сборки нескольких архитектур для всей системы на моих системах на часов . Мне еще предстоит встроить инструментальную цепочку в мой отдельный crunchgen двоичный файл, но я подозреваю, что когда я это сделаю, будет сэкономлено больше времени на сборку из-за выигрыша в кеше ЦП.

2
ответ дан Greg A. Woods 11 January 2017 в 20:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: