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

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

1) разница в производительности между статической и динамической компоновки, как правило, незначительна.

2) (1) не верно, если с помощью компилятора профилирования, которое использует данные профиля для оптимизации hotpaths программу, потому что с статическая компоновка, компилятор может оптимизировать как ваш код, так и код библиотеки. С помощью dynamic linking можно оптимизировать только ваш код. Если большая часть времени тратится на выполнение кода библиотеки, это может иметь большое значение. В противном случае (1) по-прежнему применяется.

15 ответов


  • динамический связывание может уменьшить общее потребление ресурсов (если несколько процессов используют одну и ту же библиотеку (включая версию в "том же", конечно)). Я считаю, что это аргумент, который управляет его присутствием в большинстве сред. Здесь "ресурсы" включают дисковое пространство, ОЗУ и пространство кэша. Конечно, если ваш динамический компоновщик недостаточно гибок, существует риск ада DLL.
  • динамический связывание означает, что исправления ошибок и обновления для библиотек распространение улучшение код продукт, не требуя от вас ничего грузить.
  • Плагины всегда динамический связывание.
  • Static linking, означает, что вы можете знать, что код будет работать в очень ограниченные сред (в начале процесса загрузки или в режиме спасения).
  • Static связывание может сделать бинарники легче распределить для различных пользовательских сред (за счет отправки большой и более ресурсоемкой программы).
  • Static связывание может позволить немного быстрый запуск раз, но это в некоторой степени зависит как от размера, так и от сложности вашей программы и о деталях стратегии загрузки OSs.

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


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

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

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

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

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

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


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


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

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

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

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

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

EDIT (чтобы ответить на комментарий, пользовательским подчеркиванием)

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

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

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

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

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


Я согласен с пунктами dnmckee упоминает, плюс:

  • статически связанные приложения может быть проще развернуть, так как существует меньше или нет дополнительных зависимостей файлов (.dll / .Итак) это может вызвать проблемы, когда они отсутствуют или установлены в неправильном месте.

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

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

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


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

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

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

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

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

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

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


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


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

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


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


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

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


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

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


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

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

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


static linking дает вам только один exe, чтобы внести изменения, вам нужно перекомпилировать всю вашу программу. В то время как в динамической компоновке вам нужно внести изменения только в dll и при запуске exe, изменения будут подобраны во время выполнения.Его проще предоставлять обновления и исправления ошибок с помощью динамической компоновки (например, windows).


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

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

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

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

это еще не все. Я также обычно создаю и использую установки операционной системы NetBSD для моих полных систем разработки, статически связывая все двоичные файлы. Несмотря на то, что это занимает огромное количество больше места на диске (~6.6 GB всего для x86_64 со всем, включая toolchain и X11 static-linked) (особенно если один сохраняет полные таблицы символов отладки, доступные для всех программ другой ~2.5 GB), результат по-прежнему работает быстрее в целом, а для некоторых задач даже использует меньше памяти, чем типичный динамическая система, предназначенная для совместного использования кодовых страниц библиотеки. Диск дешев (даже быстрый диск), а память для кэширования часто используемых дисковых файлов также относительно дешева, но циклы процессора действительно не являются, и платят ld.so стоимость запуска для каждого процесса, который начинается с время его запуска займет часы и часы циклов процессора от задач, которые требуют запуска многих процессов, особенно когда одни и те же программы используются снова и снова, такие как компиляторы в разработке система. Статические связанные программы toolchain могут сократить время сборки мульти-архитектуры всей ОС для моих систем на часа. Я еще не построил цепочку инструментов в свой сингл crunchgen'ed binary, но я подозреваю, что когда я это сделаю, будет больше часов времени сборки, сэкономленных из-за выигрыша для кэша процессора.

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

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

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

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