Статичне сполучення проти динамічного зв'язку


398

Чи є певні переконливі причини для вибору статичного зв’язку через динамічне з'єднання або навпаки в певних ситуаціях? Я чув або читав наступне, але я не знаю достатньо з цього приводу, щоб підтвердити його правдивість.

1) Різниця в швидкості виконання між статичним зв'язком і динамічним зв'язком зазвичай незначна.

2) (1) не відповідає дійсності, якщо використовувати компілятор профілювання, який використовує дані профілю для оптимізації гарячих шляхів програми, оскільки за допомогою статичного посилання компілятор може оптимізувати і ваш код, і код бібліотеки. За допомогою динамічного посилання можна оптимізувати лише ваш код. Якщо більша частина часу приділяється виконанню бібліотечного коду, це може мати велике значення. В іншому випадку (1) все ще діє.


59
"За допомогою статичного з'єднання компілятор може оптимізувати .. код бібліотеки", але тільки якщо він теж компілює! Якщо ви просто посилаєтесь на попередньо складені об’єктні файли, ваш компілятор не отримує шансу їх оптимізувати.

3
Якщо це правда, то ви маєте рацію, але є питання, наскільки це правда у сучасних компіляторів, якщо хтось може перевірити так чи інакше, це було б чудово.
Eloff

5
У випадку компілятора, що компілює до власного коду (як і більшість компіляторів C / C ++), немає більше шансів на оптимізацію коду. Якщо код скомпільований на якусь проміжну мову (наприклад, .Net IL), компілятор JIT викликається, коли бібліотека завантажується, щоб скомпілювати його до рідного коду. Ця кінцева компіляція може з часом розвиватися краще, оскільки компілятор JIT розвивається.
Тарідон

3
@Eloff: VS2008 робить саме це з LTCG. (Хоча файли lib стають величезними, хоча ..) Я бавився з цим, а для когось, хто цікавиться, "що мій компілятор може зробити для мене", це не дивно.
peterchen

Відповіді:


347
  • Динамічне зв’язування може зменшити загальне споживання ресурсів (якщо, звичайно, більше одного процесу поділяє одну і ту ж бібліотеку (звичайно, включаючи версію в "тій же")). Я вважаю, що це аргумент, який обумовлює його присутність у більшості середовищ. Тут "ресурси" включає простір на диску, оперативну пам'ять та кеш-простір. Звичайно, якщо ваш динамічний лінкер недостатньо гнучкий, існує ризик пекла DLL .
  • Динамічне посилання означає, що виправлення помилок та оновлення бібліотек поширюються, щоб покращити ваш продукт, не вимагаючи від вас нічого надсилати .
  • Плагіни завжди вимагають динамічного зв’язку.
  • Статичне посилання означає, що ви можете знати, що код працюватиме в дуже обмежених середовищах (на початку завантажувального процесу або в режимі порятунку).
  • Статичне зв’язування може полегшити розповсюдження двійкових файлів у різних середовищах користувача (ціною надсилання більшої та більш голодної програми).
  • Статичне зв’язування може дати трохи швидший час запуску , але це певною мірою залежить як від розміру та складності вашої програми, так і від деталей стратегії завантаження ОС.

Деякі редагування включають дуже важливі пропозиції в коментарі та інші відповіді. Я хотів би зазначити, що спосіб вирішення цього питання багато в чому залежить від того, в якому середовищі ви плануєте працювати. Мінімальні вбудовані системи можуть не мати достатньо ресурсів для підтримки динамічного зв’язку. Трохи більші невеликі системи цілком можуть підтримувати динамічне з'єднання, оскільки їх пам'ять є достатньо малою, щоб зробити оперативну пам'ять від динамічного зв’язку дуже привабливою. Як зазначає Марк, повністю роздуті споживчі ПК мають величезні ресурси, і ви, мабуть, можете дозволити питанням зручності керувати вашим мисленням з цього приводу.


Для вирішення питань ефективності та ефективності: це залежить .

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

Однак якщо у вас запущено кілька процесів, які всі називають одну і ту ж бібліотеку, ви можете закінчити збереження кеш-рядків (і, таким чином, виграти в запущеній продуктивності) при використанні динамічного посилання відносно використання статичного зв’язку. (Якщо тільки сучасні ОС не розумні, щоб помітити однакові сегменти в статично пов'язаних бінарних файлах. Здається, важко, хтось знає?)

Ще одне питання: час завантаження. Ви оплачуєте витрати на завантаження в якийсь момент. Коли ви сплатите цю вартість, залежить від того, як працює операційна система, а також того, яке з'єднання ви використовуєте. Можливо, ви краще відкладете його, поки не знаєте, що вам це потрібно.

Зауважте, що з'єднання статичного проти динамічного традиційно не є проблемою оптимізації, оскільки вони обидва включають окрему компіляцію вниз до об'єктних файлів. Однак цього не потрібно: компілятор, в принципі, може "скомпілювати" "статичні бібліотеки" до перетравленої форми AST спочатку та "пов'язувати" їх, додаючи ці AST до тих, що генеруються для основного коду, таким чином, забезпечуючи глобальну оптимізацію. Жодна із систем, які я використовую, не робить цього, тому не можу коментувати, наскільки добре вона працює.

Відповідь на запитання про ефективність завжди полягає в тестуванні (і використовуйте тестове середовище настільки, наскільки це можливо, як середовище розгортання).


24
Споживання ресурсів - це в основному простір коду, який із часом все менше викликає занепокоєння. Якщо 500 КБ бібліотеки поділяється між 5 процесами, це економія 2 Мб, що менше, ніж 1,1% 3 Гб оперативної пам’яті.
Марк Рансом

3
Якщо бібліотека також має однакове віртуальне відображення (однаковий фізичний та віртуальний адреси у всіх процесах), чи не динамічне посилання також зберігає слоти TLB в MMU процесора?
Зан Лінкс

6
Також динамічне посилання дозволяє легко оновлювати помилковий код бібліотеки з кращими версіями.
Зан Лінкс

89
@Zan Це також дозволяє легко додати баггі-код до робочої версії.

6
"Плагіни завжди вимагають динамічного з'єднання." Це неправильно. Деякі моделі плагінів, такі як AudioUnits Apple, можуть запускати плагін в окремий процес і використовувати IPC. Це більш безпечна альтернатива динамічному підключенню плагінів (плагін не може збити хост). Запропонуйте оновити відповідь до "Плагіни можуть потребувати динамічного посилання" або подібного.
Тейлор

68

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 не має у них AFAIK ні для чого іншого, ніж 15 бітових діапазонів стрибків, а не для безумовних стрибків і викликів. Ось чому функції (від генераторів), що мають більше 32 Кб, завжди були проблемою і потребували вбудованих батутів.

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

Тож ви більше не мали б жодної вигоди.

Я не пригадую, яка ОС (Solaris або FreeBSD) створювала мені проблеми з моєю системою збирання Unix, тому що я просто цього не робив і цікавився, чому вона вийшла з ладу, поки я не звернувся -fPICдо неї gcc.


4
Мені подобається ця відповідь, тому що вона була єдиною для вирішення питань, які я порушив у питанні.
Елофф

Було б цікаво мати посилання на ті технічні характеристики DLL та порівняння між різними операційними системами.
UncleZeiv

Здається добре, але швидкість процесора точно не досягла своїх меж.
Айдіакапі

67

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


17
Поки кінцевий користувач може повторно зв’язатися з кодом LGPL'd (наприклад, ви надаєте вихідний код або компільовані об’єктивні файли зі своїм програмним забезпеченням), то статичне посилання добре . Крім того, якщо ваше програмне забезпечення призначене для внутрішнього використання (тобто використовується лише в межах вашої організації, а не поширюється), ви можете статично зв’язуватися. Це стосується, наприклад, серверного програмного забезпечення, де сервер не розповсюджується.
JBentley

3
Не зрозумійте. Чи можете ви дати мені більше джерела (або детальніше), щоб оцінити те, що ви написали?
Баська

4
@Thorn див. Розділ 4.d + e щодо ліцензії LGPL . Вам або потрібно поширити у формі, яка вимагає від користувача зв’язку, або розповсюдити спільну (динамічну) бібліотеку.
Марк Викуп

46

Я погоджуюсь із пунктами, які згадує dnmckee, плюс:

  • Статично пов’язані програми можуть бути простішими у розгортанні, оскільки існує менша кількість або відсутність додаткових залежностей файлів (.dll / .so), які можуть спричинити проблеми, коли вони відсутні або встановлені в іншому місці.

6
Варто зазначити, що компілятор Go від Google буде лише статично збирати двійкові файли здебільшого з цієї причини.
Хата8

34

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

Як частина великої системи, яка будувалася та тестувалася за допомогою безперервної інтеграції, тести на нічну регресію проводились за допомогою статично пов'язаної версії виконуваних файлів. Інколи ми бачимо, що символ не розв’яжеться, а статичне посилання вийде з ладу, навіть якщо динамічно пов'язаний виконуваний файл буде посилатися успішно.

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


1
Дуже добре, я нещодавно намагався це зробити з деяким кодом, який я маю на роботі, але складання всього статично виявилося напрочуд дратівливим, і я просто здався
UncleZeiv

21

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

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

3 / Я брав участь у інших проектах, які використовували динамічне посилання для функції "легкого виправлення". Але ця "легка латка" робить розповсюдження невеликих виправлень трохи простішим і складнішим - кошмарним варіантом. Ми часто закінчувались тим, що потрібно просувати все, а також відслідковувати проблеми на сайті клієнтів, оскільки неправильна версія була лексемою.

Мій висновок полягає в тому, що я використовував статичні посилання, що виключаються:

  • для таких речей, як плагіни, які залежать від динамічного зв'язку

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

Якщо ви хочете використовувати "простий патч", я б заперечував, що бібліотеками потрібно керувати, як великі бібліотеки вище: вони повинні бути майже незалежними з визначеним ABI, який не повинен змінюватися виправленнями.


1
Деякі ОС для процесорів, що не належать до PIC, або процесорів, що дорого PIC, готують динамічні бібліотеки для завантаження за певною адресою в пам'яті, і якщо вони зможуть це зробити, вони просто відображають копію бібліотеки на кожен процес, який пов'язаний з нею. Це значно зменшує накладні витрати на PIC. Принаймні OS X та деякі дистрибутиви Linux роблять це, я не впевнений у Windows.
Ендрю Макгрегор

Дякую Ендрю, я не знав, що деякі дистрибутиви Linux використовують це. Чи є у вас посилання, яку я можу дотримуватися, або ключове слово, яке я можу шукати, щоб дізнатися більше? (FWIW Я чув, що Windows робить такий варіант, але Windows занадто далеко від моєї зони компетенції, щоб я це згадував).
AProgrammer

Я думаю, що ключове слово, яке ви шукаєте, - "попереднє посилання" - воно готує бібліотеку до швидкої завантаження за певною адресою, щоб швидше запустити програму.
Blaisorblade

20

У цьому розділі детально обговорюються спільні бібліотеки про Linux та наслідки для продуктивності.


3
+1 для посилання на хауз DSO із DSO, який повинні прочитати всі, хто робить бібліотеки в Linux.
janneb

10

У системах, схожих на Unix, динамічне зв’язування може ускладнити життя «root» для використання програми зі спільними бібліотеками, встановленими у позашляхових місцях. Це тому, що динамічний лінкер зазвичай не звертає уваги на LD_LIBRARY_PATH або його еквівалент для процесів з кореневими привілеями. Іноді статичне пов'язування економить день.

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


1
Справа LD_LIBRARY_PATHне є перешкодою для використання спільних бібліотек, принаймні, не в GNU / Linux. Наприклад, якщо ви розміщуєте спільні бібліотеки в каталозі ../lib/щодо файлу програми, тоді в ланцюзі інструментів GNU параметр linker -rpath $ORIGIN/../libвкаже пошук бібліотеки з цього відносного місця. Потім можна легко перемістити додаток разом із усіма пов'язаними спільними бібліотеками. Використовуючи цей трюк, без проблем мати кілька версій програми та бібліотек (якщо припустити, що вони пов'язані, якщо ні, ви можете використовувати символічні посилання).
FooF

> для процесів з правами root. Я думаю, що ви говорите про встановлені програми, які працюють від не-root користувачів - інакше це не має сенсу. І встановлений бінарний файл з бібліотеками в нестандартних місцях дивний - але оскільки тільки root може встановлювати ці програми, він також може редагувати /etc/ld.so.confдля цього випадку.
Blaisorblade

10

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


1
Я фактично не визначав різницю в швидкості компіляції, але я би динамічний посилання, якщо це було значно швидше. Boost робить достатньо поганих речей для моєї компіляції, як це є.
Елофф

9

Найкращий приклад для динамічного зв'язку - коли бібліотека залежить від обладнання, що використовується. У давнину математичну бібліотеку C вважали динамічною, так що кожна платформа може використовувати всі можливості процесора для її оптимізації.

Ще кращим прикладом може бути OpenGL. OpenGl - це API, який реалізується по-різному AMD та NVidia. І ви не можете використовувати реалізацію NVidia на картці AMD, оскільки апаратне забезпечення інше. Через це ви не можете статично пов’язати OpenGL зі своєю програмою. Тут використовується динамічне посилання, щоб оптимізувати API для всіх платформ.


8

Динамічне з'єднання вимагає додаткового часу, щоб ОС знаходила динамічну бібліотеку та завантажувала її. За допомогою статичного зв’язку все разом, і це одноразове завантаження в пам'ять.

Також див. Пекло DLL . Це сценарій, коли DLL, яку завантажує ОС, не є тією, що постачається з вашою програмою, або версія, яку очікує ваша програма.


1
Важливо зазначити, що існує цілий ряд контрзаходів, щоб уникнути пекла DLL.
окудо

5

Ще одне питання, яке ще не обговорювалося, - це виправлення помилок у бібліотеці.

За допомогою статичного посилання вам не тільки доведеться перебудувати бібліотеку, але доведеться повторно пов’язати і перерозподілити виконуваний файл. Якщо бібліотека просто використовується в одному виконаному файлі, це може не бути проблемою. Але чим більше виконуваних файлів, які потрібно повторно пов’язати та перерозподілити, тим більший біль.

За допомогою динамічного зв’язку ви просто відновлюєте та перерозподіляєте динамічну бібліотеку, і ви закінчите.


2

статичне посилання дає вам лише один екзе, не впорядкований, щоб внести зміни, необхідні для перекомпіляції всієї вашої програми. Тоді як при динамічному посиланні вам потрібно внести зміни лише до dll, і коли ви запускаєте свій exe, зміни вибиратимуться під час виконання. Це легше забезпечити оновлення та виправлення помилок шляхом динамічного посилання (наприклад: windows).


2

Існує велика і зростаюча кількість систем, де надзвичайний рівень статичного зв’язку може мати величезний позитивний вплив на додатки та продуктивність системи.

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

Надзвичайно поширений приклад - пристрої, що використовують системи GNU / Linux, що використовують Busybox . Я довів це до крайності за допомогою NetBSD , створивши завантажувальний системний образ i386 (32-бітний), який включає як ядро, так і його кореневу файлову систему, остання з яких містить єдину статичну пов'язану (від crunchgen) бінарну з жорсткими посиланнями на всі програми, що містять усі (добре, нарешті, 274) стандартних повнофункціональних системних програм (більшість, крім ланцюжка інструментів), і розміром є менше 20 мегабайт (і, ймовірно, працює дуже комфортно в системі лише 64 Мб пам'яті (навіть із некомпресованою кореневою файловою системою та повністю в оперативній пам’яті), хоча мені не вдалося знайти одну настільки малу, щоб перевірити її).

У попередніх публікаціях вже згадувалося, що час запуску статично пов'язаних бінарних файлів швидше (і це може бути набагато швидше), але це лише частина картини, особливо коли весь об'єктний код пов'язаний в один і той же файл, а ще більше, коли операційна система підтримує підказку запиту коду безпосередньо з виконуваного файлу. У цьому ідеальному сценарії час запуску програм буквально незначний, оскільки майже всі сторінки коду вже будуть в пам'яті та будуть використовуватися оболонкою (та initбудь-якими іншими фоновими процесами, які можуть працювати), навіть якщо запитувана програма не має коли-небудь запускався після завантаження, оскільки, можливо, потрібно завантажити лише одну сторінку пам'яті для виконання вимог програми.

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


2

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

Динамічне посилання - це те, що ви вважаєте звичайним, воно робить виконуваний файл, який все ще вимагає DLL-файлів і таких, щоб вони знаходилися в одному каталозі (або DLL-файли могли бути в системній папці).

(DLL = бібліотека динамічних посилань )

Динамічно пов'язані виконувані файли збираються швидше і не є такими важкими для ресурсів.


0

Static linking це процес під час компіляції, коли пов'язаний вміст копіюється в первинний бінарний і стає єдиним бінарним.

Мінуси:

  • час компіляції довший
  • вихід бінарний більший

Dynamic linkingце процес виконання під час завантаження пов'язаного вмісту. Ця технологія дозволяє:

  • оновити зв'язаний бінарний файл, не перекомпонувавши основного, що підвищує ABIстабільність [About]
  • мати єдину спільну копію

Мінуси:

  • час початку повільніше (пов'язаний вміст слід скопіювати)
  • Помилки лінкера кидаються під час виконання
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.