Linkowanie statyczne vs linkowanie dynamiczne

Czy istnieją przekonujące powody wydajności, aby wybrać statyczne linkowanie zamiast dynamicznego linkowania lub odwrotnie w pewnych sytuacjach? Słyszałem lub czytałem następujące, ale nie wiem wystarczająco dużo na ten temat, aby ręczyć za jego prawdziwość.

1) różnica w wydajności pracy między łączeniem statycznym a łączeniem dynamicznym jest zwykle znikoma.

2) (1) nie jest prawdą, jeśli używa się kompilatora profilującego, który wykorzystuje dane profilu do optymalizacji hotpaths programu, ponieważ przy statycznym łączeniu, kompilator może zoptymalizować zarówno Twój kod, jak i kod biblioteki. Dzięki dynamicznemu łączeniu tylko twój kod może zostać zoptymalizowany. Jeśli większość czasu poświęca się na uruchamianie kodu biblioteki, może to mieć duże znaczenie. W przeciwnym razie (1) nadal obowiązuje.

Author: Tim, 2010-01-03

15 answers

  • dynamiczne łączenie może zmniejszyć całkowite zużycie zasobów (jeśli więcej niż jeden proces dzieli tę samą bibliotekę (w tym oczywiście wersję w "tej samej")). Uważam, że jest to argument, który napędza jego obecność w większości środowisk. Tutaj "zasoby" obejmują miejsce na dysku, pamięć RAM i pamięć podręczną. Oczywiście, jeśli twój dynamiczny linker nie jest wystarczająco elastyczny, istnieje ryzyko DLL hell .
  • dynamiczne linkowanie oznacza, że poprawki błędów i uaktualnienia do bibliotek propagują , aby ulepszyć twój produkt bez konieczności wysyłania czegokolwiek.
  • Wtyczkizawsze wywołują połączenie dynamiczne.
  • statyczne łączenie oznacza, że możesz wiedzieć, że kod będzie działał w bardzo ograniczonych środowiskach (na początku procesu rozruchowego lub w trybie ratunkowym).
  • statyczne linkowanie może sprawić, że binaria będą łatwiejsze do dystrybucji w różnych środowiskach użytkowników (kosztem wysłania duży i bardziej zasobny program).
  • statyczne łączenie może pozwolić na nieco szybsze uruchamianie razy, ale zależy to w pewnym stopniu zarówno od wielkości i złożoności Twojego programu i od szczegółów strategii ładowania OSs.

Niektóre edycje, aby uwzględnić bardzo istotne sugestie w komentarzach i innych odpowiedziach. Chciałbym zauważyć, że sposób, w jaki się przełamiesz, zależy w dużej mierze od tego, w jakim środowisku planujesz działać. Minimalne systemy wbudowane mogą nie mieć wystarczających zasobów do obsługi dynamicznego łączenia. Nieco większe małe systemy mogą również wspierać łączenie, ponieważ ich pamięć jest na tyle mała, że oszczędności RAM wynikające z dynamicznego łączenia są bardzo atrakcyjne. Pełnowymiarowe Komputery konsumenckie mają, jak zauważa Mark, ogromne zasoby i prawdopodobnie możesz pozwolić, aby problemy z wygodą napędzały Cię do myślenia w tej sprawie.


Aby rozwiązać problemy wydajności i wydajności: to zależy .

Klasycznie, biblioteki dynamiczne wymagają pewnego rodzaju warstwy kleju, co często oznacza podwójną wysyłkę lub dodatkową warstwę indrection w adresowaniu funkcji i może kosztować trochę szybkości (ale czy czas wywołania funkcji jest w rzeczywistości dużą częścią twojego czasu pracy???).

Jednakże, jeśli uruchamiasz wiele procesów, które często wywołują tę samą bibliotekę, możesz zapisać linie pamięci podręcznej (a tym samym zyskać na wydajności działania) podczas używania dynamicznego linkowania względnego przy użyciu linkowania statycznego. (Chyba że nowoczesny OS są wystarczająco inteligentne, aby zauważyć identyczne segmenty w statycznie powiązanych binariach. Wydaje się trudne, ktoś wie?)

Kolejna kwestia: czas ładowania. W pewnym momencie płacisz koszty załadunku. Kiedy płacisz ten koszt zależy od tego, jak działa system operacyjny, a także tego, czego używasz. Może lepiej odkładaj spłatę, dopóki nie będziesz wiedział, że jej potrzebujesz.

Zauważ, że linkowanie statyczne-vs--dynamiczne jest tradycyjnie , a nie problemem optymalizacji, ponieważ oba wymagają oddzielnej kompilacji aż do obiektu pliki. Jednak nie jest to wymagane: kompilator może w zasadzie "skompilować" "biblioteki statyczne" do wstępnie przetrawionej postaci AST i "połączyć" je dodając te AST do tych wygenerowanych dla głównego kodu, umożliwiając w ten sposób globalną optymalizację. Żaden z systemów, których używam nie robi tego, więc nie mogę skomentować, jak dobrze to działa.

Sposób odpowiadania na pytania dotyczące wydajności jest zawsze poprzez testowanie(i używanie środowiska testowego tak bardzo, jak to tylko możliwe).

 309
Author: dmckee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-26 01:18:41

Dynamiczne łączenie jest jedynym praktycznym sposobem spełnienia niektórych wymagań licencyjnych, takich jak LGPL.

 63
Author: Mark Ransom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-03 05:01:09

1) opiera się na fakcie, że wywołanie funkcji DLL jest zawsze za pomocą dodatkowego skoku pośredniego. Obecnie jest to zwykle znikome. Wewnątrz biblioteki DLL jest trochę więcej narzutu na procesory i386, ponieważ nie mogą one wygenerować kodu niezależnego od pozycji. Na amd64 skoki mogą być względne do licznika programu, więc jest to ogromna poprawa.

2) to jest poprawne. Dzięki optymalizacjom prowadzonym przez profilowanie zwykle można wygrać około 10-15 procent wydajności. Teraz, gdy prędkość procesora osiągnęła swoje granice może warto to zrobić.

Dodałbym: (3) linker może zorganizować funkcje w bardziej efektywnym grupowaniu pamięci podręcznej, tak aby kosztowne błędy poziomu pamięci podręcznej były zminimalizowane. Może to również szczególnie wpływać na czas uruchamiania aplikacji (na podstawie wyników, które widziałem z kompilatorem Sun C++)

I nie zapominaj, że z DLL nie można usunąć martwego kodu. W zależności od języka kod DLL może również nie być optymalny. Funkcje wirtualne są zawsze wirtualne ponieważ kompilator nie wie, czy klient go nadpisuje.

Z tych powodów, w przypadku, gdy nie ma potrzeby korzystania z bibliotek DLL, po prostu użyj statycznej kompilacji.

EDIT (aby odpowiedzieć na komentarz, przez użytkownika)

Oto dobre źródło informacji o problemie z kodem niezależnym od pozycji http://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/{[18]

Jak wyjaśniono x86 nie ma ich AFAIK do niczego innego niż 15 zakres skoków bitowych i nie dla bezwarunkowych skoków i wywołań. Dlatego też funkcje (z generatorów) mające więcej niż 32K zawsze stanowiły problem i wymagały wbudowanych trampolin.

Ale na popularnym systemie operacyjnym x86, takim jak Linux, po prostu nie musisz się przejmować, czy plik SO/DLL nie jest generowany za pomocą przełącznika gcc -fpic (co wymusza użycie pośrednich tabel skoków). Bo jeśli tego nie zrobisz, kod jest po prostu naprawiony, tak jak normalny linker by go przeniósł. Ale robiąc to sprawia, że kod segment nie do udostępnienia i wymagałoby pełnego odwzorowania kodu z dysku do pamięci i dotknięcia Go wszystkich, zanim będzie można go użyć (opróżnienie większości pamięci podręcznych, naciśnięcie TLB) itp. Był czas, kiedy uważano to za powolne ... za wolno.

Więc nie miałbyś już żadnych korzyści.

Nie przypominam sobie, jaki system operacyjny (Solaris czy FreeBSD) sprawiał mi problemy z moim uniksowym systemem budowania, ponieważ po prostu tego nie robiłem i zastanawiałem się, dlaczego się rozbił, dopóki nie zastosowałem -fPIC do gcc.

 62
Author: Lothar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-03 02:20:47

Zgadzam się z punktami, o których wspomina dnmckee, plus:

  • statycznie powiązane aplikacje mogą być łatwiejsze do wdrożenia, ponieważ istnieje mniej lub nie ma dodatkowych zależności od plików (.dll / .so), które mogą powodować problemy, gdy brakuje ich lub są zainstalowane w niewłaściwym miejscu.
 42
Author: stakx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-03 00:16:24

Jednym z powodów wykonania statycznie powiązanej kompilacji jest sprawdzenie, czy masz pełne zamknięcie dla pliku wykonywalnego, tzn. czy wszystkie odwołania do symboli są rozwiązane poprawnie.

Jako część dużego systemu, który był budowany i testowany przy użyciu ciągłej integracji, nocne testy regresji były uruchamiane przy użyciu statycznie połączonej wersji plików wykonywalnych. Czasami widzimy, że symbol nie rozwiązuje się, a łącze statyczne zawiedzie, nawet jeśli dynamicznie połączony plik wykonywalny połączy się z powodzeniem.

Miało to Zwykle miejsce, gdy symbole głęboko osadzone we wspólnych bibliotekach miały błędnie zapisaną nazwę, więc nie łączyły się statycznie. Łącznik dynamiczny nie rozwiązuje całkowicie wszystkich symboli, niezależnie od użycia oceny depth-first lub broadhth-first, więc można zakończyć z dynamicznie połączonym plikiem wykonywalnym, który nie ma pełnego zamknięcia.

 31
Author: Rob Wells,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-22 18:55:31

1/ byłem na projektach, w których dynamiczne linkowanie vs linkowanie statyczne było porównywane i różnica nie została określona na tyle mała, aby przełączyć się na dynamiczne linkowanie (nie byłem częścią testu, po prostu znam wniosek)

2 / dynamiczne linkowanie jest często związane z PIC (Kod niezależny od pozycji, kod, który nie musi być modyfikowany w zależności od adresu, pod którym jest ładowany). W zależności od architektury PIC może przynieść kolejne spowolnienie, ale jest potrzebne, aby uzyskać korzyści współdzielenia dynamicznie połączonej biblioteki pomiędzy dwoma plikami wykonywalnymi (a nawet dwoma procesami tego samego pliku wykonywalnego, jeśli system operacyjny używa randomizacji adresu obciążenia jako środka bezpieczeństwa). Nie jestem pewien, czy wszystkie OS pozwalają na oddzielenie tych dwóch pojęć, ale Solaris i Linux robią i ISTR, że HP-UX również.

3 / byłem w innych projektach, które używały dynamicznego linkowania dla funkcji "easy patch". Ale ta "łatwa łatka" sprawia, że dystrybucja małych poprawek jest trochę łatwiejsza i skomplikowana koszmar wersjonowania. Często kończyliśmy na tym, że musieliśmy naciskać wszystko plus śledzić problemy na stronie klienta, ponieważ zła wersja była tokenem.

Mój wniosek jest taki, że użyłem linkowania statycznego:

  • Dla rzeczy takich jak wtyczki, które zależą od dynamicznego łączenia

  • Gdy udostępnianie jest ważne (duże biblioteki używane przez wiele procesów w tym samym czasie, jak C / C++ runtime, biblioteki GUI, ... które często są zarządzane niezależnie i dla których ABI jest ściśle zdefiniowane)

Jeśli ktoś chce użyć "łatki łatwej", argumentowałbym, że biblioteki muszą być zarządzane tak, jak duże biblioteki powyżej: muszą być prawie niezależne z zdefiniowanym ABI, które nie mogą być zmieniane przez poprawki.

 21
Author: AProgrammer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-03 10:03:18

To bardzo szczegółowo omawia biblioteki współdzielone na Linuksie i impliaction wydajności.

 19
Author: nos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-03 00:17:14

W systemach uniksopodobnych, dynamiczne łączenie może utrudnić "root' owi " korzystanie z aplikacji z bibliotekami współdzielonymi zainstalowanymi w miejscach niedostępnych. Dzieje się tak, ponieważ dynamiczny linker zazwyczaj nie zwraca uwagi na LD_LIBRARY_PATH lub jego odpowiednik dla procesów z uprawnieniami roota. Czasami statyczne łączenie ratuje sytuację.

Alternatywnie, proces instalacji musi zlokalizować biblioteki, ale może to utrudnić wiele wersji oprogramowanie do współistnienia na maszynie.

 10
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-04 19:15:05

To bardzo proste, naprawdę. Kiedy dokonujesz zmiany w kodzie źródłowym, chcesz czekać 10 minut na jego zbudowanie lub 20 sekund? 20 sekund to wszystko, co mogę znieść. Poza tym albo wyciągam miecz, albo zaczynam myśleć o tym, jak mogę użyć oddzielnej kompilacji i linkowania, aby przywrócić ją do strefy komfortu.

 10
Author: Hans Passant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-03 08:49:24

Dynamiczne łączenie wymaga dodatkowego czasu dla systemu operacyjnego, aby znaleźć bibliotekę dynamiczną i załadować ją. Dzięki statycznemu łączeniu wszystko jest razem i jest to jednorazowe obciążenie pamięci.

Zobacz też DLL Hell . Jest to scenariusz, w którym biblioteka DLL ładowana przez system operacyjny nie jest tą, która została dostarczona wraz z aplikacją lub wersją, której oczekuje aplikacja.

 7
Author: Thomas Matthews,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-03 00:40:53

Najlepszym przykładem dynamicznego łączenia jest, gdy Biblioteka jest zależna od używanego sprzętu. W starożytności postanowiono, że biblioteka matematyki C będzie dynamiczna, tak aby każda platforma mogła wykorzystać wszystkie możliwości procesora do jej optymalizacji.

Jeszcze lepszym przykładem może być OpenGL. OpenGl jest API, które jest zaimplementowane inaczej przez AMD i NVidia. I nie jesteś w stanie użyć implementacji Nvidii na karcie AMD, ponieważ sprzęt jest inny. Nie można łączyć statycznie OpenGL ze swoim program, z tego powodu. Dynamiczne łączenie jest tutaj używane, aby umożliwić optymalizację API dla wszystkich platform.

 7
Author: Arne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-12 01:52:32

Kolejna kwestia, o której jeszcze nie dyskutowano, to naprawianie błędów w bibliotece.

Przy łączeniu statycznym, nie tylko trzeba odbudować bibliotekę, ale trzeba będzie ponownie połączyć i ponownie przypisać plik wykonywalny. Jeśli Biblioteka jest używana tylko w jednym pliku wykonywalnym, może to nie być problem. Ale im więcej plików wykonywalnych trzeba ponownie połączyć i rozpowszechnić, tym większy jest ból.

Z dynamicznym linkowaniem, po prostu przebudowujesz i redystrybuujesz dynamiczną bibliotekę i gotowe.

 5
Author: R Samuel Klatchko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-03 08:31:46

Statyczne linkowanie daje Ci tylko jeden exe, aby dokonać zmiany musisz przekompilować cały program. Podczas gdy w dynamicznym łączeniu musisz dokonać zmiany tylko w dll i gdy uruchomisz swój exe, zmiany zostaną odebrane w czasie wykonywania.Łatwiej jest zapewnić aktualizacje i poprawki błędów poprzez dynamiczne łączenie (np.

 3
Author: Govardhan Murali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-11 01:35:30

Istnieje ogromna i rosnąca liczba systemów, w których ekstremalny poziom statycznego łączenia może mieć ogromny pozytywny wpływ na aplikacje i wydajność systemu.

[4]}mam na myśli to, co często nazywa się "systemami wbudowanymi", z których wiele w coraz większym stopniu korzysta z systemów operacyjnych ogólnego przeznaczenia, a systemy te są używane do wszystkiego, co można sobie wyobrazić.

Niezwykle powszechnym przykładem są urządzenia korzystające z systemów GNU/Linux korzystających z Busybox . Zabrałem to do extreme z NetBSD poprzez zbudowanie bootowalnego (32-bitowego) obrazu systemu i386, który zawiera zarówno jądro, jak i główny system plików, ten ostatni, który zawiera pojedynczy statyczny (przez crunchgen) binarny z twardymi linkami do wszystkich programów, który sam zawiera wszystkie (w końcu liczy 274) standardowych programów systemowych z pełnymi funkcjami (większość z wyjątkiem toolchain), i jest mniejszy niż 20 megabajtów w rozmiarze (i prawdopodobnie działa bardzo wygodnie w systemie z zaledwie 64MB pamięci (nawet z głównym systemem plików nieskompresowany i całkowicie w pamięci RAM), choć nie byłem w stanie znaleźć taki mały, aby go przetestować).

We wcześniejszych postach wspomniano, że czas uruchamiania statycznych binariów jest szybszy (i może to być lot szybszy), ale jest to tylko część obrazu, zwłaszcza gdy cały kod obiektowy jest połączony z tym samym plikiem, a tym bardziej zwłaszcza gdy system operacyjny obsługuje żądanie stronicowania kodu bezpośrednio z pliku wykonywalnego. W ten idealny scenariusz czas uruchamiania programów jest dosłownie nieistotny, ponieważ prawie wszystkie strony kodu będą już w pamięci i będą używane przez powłokę (oraz init wszelkie inne procesy w tle, które mogą być uruchomione), nawet jeśli żądany program nigdy nie został uruchomiony od czasu rozruchu, ponieważ być może tylko jedna strona pamięci musi być załadowana, aby spełnić wymagania programu.

Jednak to wciąż nie jest cała historia. Zwykle też buduję i używam System operacyjny NetBSD instaluje się dla moich pełnych systemów programistycznych poprzez statyczne łączenie wszystkich binariów. Mimo że zajmuje to znacznie więcej miejsca na dysku (łącznie~6.6 GB dla x86_64 ze wszystkim, w tym toolchain i X11 static-linked) (zwłaszcza jeśli utrzymuje się pełne tabele symboli debugowania dostępne dla wszystkich programów, inne ~2.5 GB), wynik nadal działa szybciej ogólnie, a dla niektórych zadań nawet zużywa mniej pamięci niż typowy system dynamicznie połączony, który rzekomo udostępnia strony kodu biblioteki. Dysk jest tani (nawet szybki), a pamięć do pamięci podręcznej często używanych plików dyskowych jest również stosunkowo tania, ale cykle procesora tak naprawdę nie są, a płacenie ld.so kosztów startowych za każdy proces, który się uruchamia każdy {[20] } Czas jego uruchomienia zajmie wiele godzin cykli procesora od zadań, które wymagają uruchomienia wielu procesów, zwłaszcza gdy te same programy są używane w kółko, takie jak kompilatory w systemie deweloperskim. Statyczne programy toolchain mogą zmniejszyć cały system operacyjny czas budowania wielu architektur dla moich Systemów przez godziny. Muszę jeszcze zbudować toolchain do mojego pojedynczego crunchgen'ED binary, ale podejrzewam, że kiedy to zrobię, będzie więcej godzin czasu kompilacji zaoszczędzonych ze względu na wygraną dla pamięci podręcznej procesora.

 2
Author: Greg A. Woods,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-28 22:19:30

Statyczne łączenie zawiera pliki, których potrzebuje program w jednym pliku wykonywalnym.

Dynamiczne łączenie jest tym, co uważasz za zwykłe, tworzy plik wykonywalny, który nadal wymaga bibliotek DLL i takich, aby znajdowały się w tym samym katalogu (lub biblioteki dll mogą znajdować się w folderze systemowym).

(DLL = dynamic link library)

Dynamicznie połączone pliki wykonywalne są kompilowane szybciej i nie są tak obciążone zasobami.

 1
Author: Nykal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-05 12:01:51