Существуют ли какие-либо веские причины для выбора статического соединения вместо динамического или наоборот в определенных ситуациях? Я слышал или читал следующее, но я не знаю достаточно по этому вопросу, чтобы ручаться за его правдивость.
1) Разница в производительности во время выполнения между статической и динамической связью обычно незначительна.
2) (1) неверно, если используется профилирующий компилятор, который использует данные профиля для оптимизации горячих путей программы, поскольку при статическом связывании компилятор может оптимизировать как ваш код, так и код библиотеки. С динамическим связыванием может быть оптимизирован только ваш код. Если большую часть времени тратится на выполнение кода библиотеки, это может иметь большое значение. В противном случае (1) все еще применяется.
Ответы:
Некоторые правки включают очень важные предложения в комментариях и других ответах. Я хотел бы отметить, что способ решения этой проблемы во многом зависит от среды, в которой вы планируете работать. Минимальные встроенные системы могут не иметь достаточно ресурсов для поддержки динамического связывания. Небольшие по размеру небольшие системы вполне могут поддерживать динамическое связывание, поскольку их память достаточно мала, чтобы сделать экономию ОЗУ от динамического связывания очень привлекательной. Полноценные потребительские ПК обладают, как отмечает Марк, огромными ресурсами, и вы, вероятно, можете позволить проблемам удобства побудить вас задуматься над этим вопросом.
Для решения вопросов производительности и эффективности: это зависит .
Классически, для динамических библиотек требуется какой-то слой клея, который часто означает двойную диспетчеризацию или дополнительный уровень косвенности при адресации функций и может стоить небольшой скорости (но является ли время вызова функции большой частью вашего времени работы ???).
Однако, если вы запускаете несколько процессов, которые все вызывают одну и ту же библиотеку много раз, вы можете в конечном итоге сохранить строки кэша (и, таким образом, выиграть в производительности) при использовании динамического связывания относительно статического связывания. (Если современные ОС не достаточно умны, чтобы замечать идентичные сегменты в статически связанных двоичных файлах. Кажется, сложно, кто-нибудь знает?)
Еще одна проблема: время загрузки. Вы оплачиваете расходы на погрузку в какой-то момент. Когда вы платите, эта стоимость зависит от того, как работает ОС, а также от того, какие ссылки вы используете. Возможно, вы бы предпочли отложить оплату, пока не узнаете, что вам это нужно.
Обратите внимание, что статическое-динамическое связывание традиционно не является проблемой оптимизации, поскольку они оба включают отдельную компиляцию вплоть до объектных файлов. Однако это не требуется: компилятор в принципе может «скомпилировать» «статические библиотеки» вначале в переваренную форму AST и «связать» их, добавив эти AST к тем, которые сгенерированы для основного кода, что расширяет возможности глобальной оптимизации. Ни одна из систем, которые я использую, не делает этого, поэтому я не могу комментировать, насколько хорошо это работает.
Чтобы ответить на вопросы о производительности, всегда используйте тестирование (и используйте тестовую среду настолько, насколько это возможно, среду развертывания).
источник
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-libraries/
Как объяснялось в x86, они не имеют AFAIK ни для чего другого, кроме 15-битных диапазонов перехода, а не для безусловных переходов и вызовов. Вот почему функции (от генераторов), имеющие более 32K, всегда были проблемой и нуждались во встроенных батутах.
Но в популярных x86 ОС, таких как Linux, вам не нужно заботиться о том, что файл .so / DLL не генерируется с помощью
gcc
коммутатора-fpic
(что обеспечивает использование таблиц косвенных переходов ). Потому что, если вы этого не сделаете, код просто исправлен, как обычный компоновщик переместит его. Но при этом он делает сегмент кода недоступным для совместного использования, и ему потребуется полное отображение кода с диска в память и касание всего этого, прежде чем его можно будет использовать (очистка большей части кэшей, обращение к TLB) и т. Д. Было время когда это считалось медленным.Таким образом, вы не получили бы никакой пользы.
Я не помню, какая ОС (Solaris или FreeBSD) доставляла мне проблемы с моей системой сборки Unix, потому что я просто не делал этого и задавался вопросом, почему она рухнула, пока я не обратился
-fPIC
к нейgcc
.источник
Динамическое связывание является единственным практическим способом удовлетворения некоторых лицензионных требований, таких как LGPL .
источник
Я согласен с тем, что упоминает dnmckee, плюс:
источник
Одна из причин сделать статически связанную сборку - это убедиться, что у вас есть полное закрытие для исполняемого файла, то есть все ссылки на символы разрешены правильно.
Как часть большой системы, которая строилась и тестировалась с использованием непрерывной интеграции, ночные регрессионные тесты выполнялись с использованием статически связанной версии исполняемых файлов. Иногда мы видим, что символ не разрешается, и статическая ссылка не работает, даже если динамически связанный исполняемый файл успешно соединится.
Это обычно происходило, когда символы, которые были глубоко укоренены в общих библиотеках, имели имя с ошибкой и поэтому не имели статической связи. Динамический компоновщик не полностью разрешает все символы, независимо от использования оценки по глубине или ширине, поэтому вы можете получить динамически связанный исполняемый файл, который не имеет полного закрытия.
источник
1 / Я участвовал в проектах, в которых динамическое связывание сравнивалось со статическим связыванием, и разница не была определена достаточно мала, чтобы перейти на динамическое связывание (я не участвовал в тестировании, я просто знаю вывод)
2 / Динамическое связывание часто ассоциируется с PIC (позиционно-независимый код, код, который не нужно изменять в зависимости от адреса, по которому он загружен). В зависимости от архитектуры PIC может привести к другому замедлению, но это необходимо для того, чтобы получить преимущество совместного использования динамически связанной библиотеки между двумя исполняемыми файлами (и даже двумя процессами одного исполняемого файла, если ОС использует рандомизацию адреса загрузки в качестве меры безопасности). Я не уверен, что все ОС позволяют разделить две концепции, но Solaris и Linux делают и ISTR, что делает HP-UX также.
3 / Я был в других проектах, которые использовали динамическое связывание для функции «простого патча». Но этот «легкий патч» делает распространение небольших исправлений немного проще и сложнее, чем кошмар версий. Мы часто заканчивали тем, что нам приходилось все настаивать, а также отслеживать проблемы на сайте клиента, потому что была указана неправильная версия.
Я пришел к выводу, что я использовал статические ссылки исключены:
для таких вещей, как плагины, которые зависят от динамического связывания
когда важно совместное использование (большие библиотеки, используемые несколькими процессами одновременно, такие как среда выполнения C / C ++, библиотеки GUI, ... которые часто управляются независимо и для которых строго определен ABI)
Если кто-то хочет использовать «легкий патч», я бы сказал, что библиотеки должны управляться так же, как и большие библиотеки, описанные выше: они должны быть почти независимыми с определенным ABI, который не должен изменяться исправлениями.
источник
Это обсуждение очень подробно об общих библиотеках на linux и влиянии на производительность.
источник
В Unix-подобных системах динамическое связывание может усложнить использование «root» приложения с общими библиотеками, установленными в удаленных местах. Это связано с тем, что динамический компоновщик обычно не обращает внимания на LD_LIBRARY_PATH или его эквивалент для процессов с привилегиями root. Иногда статическое связывание спасает день.
Кроме того, процесс установки должен найти библиотеки, но это может затруднить сосуществование нескольких версий программного обеспечения на компьютере.
источник
LD_LIBRARY_PATH
это не является препятствием для использования общих библиотек, по крайней мере, в GNU / Linux. Например, если вы поместите общие библиотеки в каталог../lib/
относительно файла программы, то с помощью цепочки инструментов GNU опция компоновщика-rpath $ORIGIN/../lib
будет указывать на поиск библиотеки из этого относительного местоположения. Затем вы можете легко переместить приложение вместе со всеми связанными общими библиотеками. Используя этот трюк, можно без проблем иметь несколько версий приложения и библиотек (при условии, что они связаны, если нет, вы можете использовать символические ссылки)./etc/ld.so.conf
для этого случая.Это довольно просто, правда. Когда вы вносите изменения в свой исходный код, вы хотите подождать 10 минут для его сборки или 20 секунд? Двадцать секунд - это все, что я могу вынести. Кроме того, я либо достаю меч, либо начинаю думать о том, как я могу использовать отдельную компиляцию и связывание, чтобы вернуть его в зону комфорта.
источник
Лучший пример динамической компоновки - когда библиотека зависит от используемого оборудования. В древние времена математическая библиотека C считалась динамичной, поэтому каждая платформа могла использовать все возможности процессора для ее оптимизации.
Еще лучшим примером может быть OpenGL. OpenGl - это API, который по-разному реализован AMD и NVidia. И вы не можете использовать реализацию NVidia на карте AMD, потому что аппаратное обеспечение отличается. Из-за этого вы не можете статически связать OpenGL с вашей программой. Динамическое связывание используется здесь, чтобы позволить API быть оптимизированным для всех платформ.
источник
Динамическое связывание требует от ОС дополнительного времени, чтобы найти динамическую библиотеку и загрузить ее. Со статической связью все вместе, и это однократная загрузка в память.
Также см. DLL Ад . Это сценарий, в котором загружаемая ОС не является той, которая поставляется с вашим приложением, или той версией, которую ожидает ваше приложение.
источник
Еще одна проблема, которая еще не обсуждалась, - это исправление ошибок в библиотеке.
При статическом связывании вам нужно не только перестроить библиотеку, но и заново связать и перераспределить исполняемый файл. Если библиотека только используется в одном исполняемом файле, это может не быть проблемой. Но чем больше исполняемых файлов необходимо повторно связать и перераспределить, тем больше будет боль.
С динамическим связыванием вы просто перестраиваете и перераспределяете динамическую библиотеку, и все готово.
источник
Статическое связывание дает вам только один exe-файл, чтобы внести изменения, необходимые для перекомпиляции всей вашей программы. Принимая во внимание, что при динамическом связывании вам нужно вносить изменения только в dll, и когда вы запускаете свой exe-файл, изменения будут восприниматься во время выполнения. Проще предоставлять обновления и исправления ошибок с помощью динамического связывания (например, windows).
источник
Существует огромное и все большее число систем, в которых экстремальный уровень статического связывания может оказать огромное положительное влияние на приложения и производительность системы.
Я имею в виду то, что часто называют «встроенными системами», многие из которых все чаще используют операционные системы общего назначения, и эти системы используются для всего, что только можно себе представить.
Чрезвычайно распространенным примером являются устройства, использующие системы GNU / Linux, использующие Busybox . Я довел это до крайности с помощью NetBSD , создав загрузочный системный образ i386 (32-разрядный), который включает как ядро, так и его корневую файловую систему, причем последний содержит один
crunchgen
двоичный файл со статической связью (by ) с жесткими ссылками на все программы , которые сам содержит все (ну , по последним подсчетам 274) стандартных системных программ полнофункциональных (большинство за исключением инструментария , ), и это меньше , чем 20 мег байт (и , вероятно , работает очень комфортно в системе только с 64 МБ памяти (даже с несжатой корневой файловой системой и полностью в оперативной памяти), хотя я не смог найти такую маленькую для тестирования).В более ранних публикациях упоминалось, что время запуска двоичных файлов со статической связью быстрее (и может быть намного быстрее), но это только часть картины, особенно когда весь объектный код связан в одном и том же файл, и особенно, если операционная система поддерживает подкачку кода по требованию непосредственно из исполняемого файла. В этом идеальном сценарии время запуска программ буквально пренебрежимо мало, поскольку почти все страницы кода уже будут в памяти и будут использоваться оболочкой (и
init
любыми другими фоновыми процессами, которые могут выполняться), даже если запрошенная программа не никогда не запускался с момента загрузки, поскольку, возможно, для выполнения требований программы во время выполнения необходимо загрузить только одну страницу памяти.Однако это еще не вся история. Я также обычно собираю и использую установки операционной системы NetBSD для своих систем полной разработки, статически связывая все двоичные файлы. Несмотря на то, что для этого требуется значительно больше дискового пространства (всего ~ 6,6 ГБ для x86_64 со всем, включая набор инструментов и статическую привязку X11) (особенно если одна таблица полных символов отладки доступна для всех программ, другая ~ 2,5 ГБ), результат все равно остается в целом работает быстрее, а для некоторых задач даже использует меньше памяти, чем обычная система с динамической связью, которая предназначена для совместного использования кодовых страниц библиотеки. Диск дешев (даже быстрый диск), и память для кеширования часто используемых файлов на диске также относительно дешева, но циклы ЦП на самом деле не такие, и они платят
ld.so
стоимость запуска каждого процесса, который запускается каждыйвремя его запуска займет часы и часы циклов ЦП от задач, требующих запуска многих процессов, особенно когда одни и те же программы используются снова и снова, например, компиляторы в системе разработки. Связанные со статическими данными программы могут уменьшить время сборки моих архитектур для нескольких ОС на несколько часов . Мне еще предстоит встроить набор инструментов в мой отдельныйcrunchgen
двоичный файл, но я подозреваю, что когда я это сделаю, будет сэкономлено больше времени на сборку из-за выигрыша в кеше процессора.источник
Статическое связывание включает файлы, которые нужны программе в одном исполняемом файле.
Динамическое связывание - это то, что вы считаете обычным: оно создает исполняемый файл, для которого все еще требуются библиотеки DLL, и они должны находиться в одном каталоге (или библиотеки DLL могут находиться в системной папке).
(DLL = библиотека динамических ссылок )
Динамически связанные исполняемые файлы компилируются быстрее и не так ресурсоемки.
источник
Static linking
это процесс во время компиляции, когда связанный контент копируется в основной двоичный файл и становится единым двоичным файлом.Минусы:
Dynamic linking
это процесс во время выполнения, когда загружается связанный контент. Эта техника позволяет:ABI
стабильность [О программе]Минусы:
источник