it-swarm.com.de

Statische Verknüpfung vs. dynamische Verknüpfung

Gibt es überzeugende Gründe für die Leistung, statische Verknüpfungen in bestimmten Situationen dynamischen Verknüpfungen vorzuziehen oder umgekehrt? Ich habe Folgendes gehört oder gelesen, aber ich weiß nicht genug darüber, um für seine Richtigkeit zu bürgen.

1) Der Unterschied in der Laufzeitleistung zwischen statischer und dynamischer Verknüpfung ist normalerweise vernachlässigbar.

2) (1) ist nicht wahr, wenn ein Profilerstellungs-Compiler verwendet wird, der Profildaten zur Optimierung von Programm-Hotpaths verwendet, da der Compiler bei statischer Verknüpfung sowohl Ihren Code als auch den Bibliothekscode optimieren kann. Mit dynamischer Verknüpfung kann nur Ihr Code optimiert werden. Wenn die meiste Zeit mit dem Ausführen von Bibliothekscode verbracht wird, kann dies einen großen Unterschied bewirken. Ansonsten gilt (1) weiterhin.

374
Eloff
  • Dynamische Verknüpfungen können den Ressourcenverbrauch insgesamt reduzieren (wenn mehr als eine Prozessfreigabe vorliegt) die gleiche Bibliothek (einschließlich der Version in "der gleichen" natürlich)). Ich glaube, das ist das Argument, das es in den meisten Umgebungen ausmacht. Hier umfasst "Ressourcen" Festplattenspeicher, RAM und Cache-Speicher. Wenn Ihr dynamischer Linker nicht flexibel genug ist, besteht natürlich die Gefahr von DLL-Hölle .
  • Dynamische Verknüpfung bedeutet, dass Fehlerbehebungen und Upgrades für Bibliotheken verbreitet werden, um = zu verbessern Ihr Produkt, ohne dass Sie etwas versenden müssen.
  • Plugins erfordern immer eine dynamische Verknüpfung.
  • Statische Verknüpfung bedeutet, dass Sie wissen, dass der Code in sehr eingeschränkten Umgebungen ausgeführt wird (zu Beginn des Startvorgangs oder im Rettungsmodus).
  • Durch statische Verknüpfungen können Binärdateien leichter an verschiedene Benutzerumgebungen (unter) verteilt werden die Kosten für den Versand eines größeren und ressourcenintensiveren Programms).
  • Statische Verknüpfungen ermöglichen möglicherweise geringfügig schnellere Startzeiten , dies hängt jedoch von einigen ab Grad auf die Größe und Komplexität Ihres Programms und auf die Details der Ladestrategie des Betriebssystems.

Einige Änderungen, um die sehr relevanten Vorschläge in die Kommentare und in andere Antworten aufzunehmen. Ich möchte darauf hinweisen, dass die Art und Weise, wie Sie dies unterbrechen, stark von der Umgebung abhängt, in der Sie vorhaben, ausgeführt zu werden. Minimale eingebettete Systeme verfügen möglicherweise nicht über genügend Ressourcen, um dynamische Verknüpfungen zu unterstützen. Etwas größere kleine Systeme unterstützen möglicherweise dynamische Verknüpfungen, da ihr Speicher klein genug ist, um die RAM Einsparungen durch dynamische Verknüpfungen sehr attraktiv zu machen. Ausgereifte Consumer-PCs verfügen, wie Mark bemerkt, über enorme Ressourcen, und Sie können sich wahrscheinlich von den praktischen Aspekten anregen lassen, über diese Angelegenheit nachzudenken.


Zur Behebung der Leistungs- und Effizienzprobleme: Es kommt darauf an .

Klassischerweise erfordern dynamische Bibliotheken eine Art Klebeschicht, was häufig doppelten Versand oder eine zusätzliche Indirektionsebene bei der Funktionsadressierung bedeutet und ein wenig Geschwindigkeit kosten kann (aber macht die Funktionsaufrufzeit tatsächlich einen großen Teil Ihrer Laufzeit aus ???).

Wenn Sie jedoch mehrere Prozesse ausführen, die alle häufig dieselbe Bibliothek aufrufen, können Sie beim Verwenden dynamischer Verknüpfungen im Vergleich zur Verwendung statischer Verknüpfungen Cache-Zeilen einsparen (und damit die Leistung verbessern). (Es sei denn, moderne Betriebssysteme sind intelligent genug, um identische Segmente in statisch verknüpften Binärdateien zu erkennen. Scheint schwer zu sein, weiß jemand?)

Ein weiteres Problem: Ladezeit. Sie bezahlen irgendwann die Ladekosten. Wann Sie diese Kosten bezahlen, hängt davon ab, wie das Betriebssystem funktioniert und welche Verknüpfungen Sie verwenden. Vielleicht zögern Sie lieber, es zu bezahlen, bis Sie wissen, dass Sie es brauchen.

Beachten Sie, dass statische und dynamische Verknüpfungen traditionell nicht ein Optimierungsproblem darstellen, da beide eine separate Kompilierung bis hinunter zu Objektdateien beinhalten. Dies ist jedoch nicht erforderlich: Ein Compiler kann im Prinzip "statische Bibliotheken" zu einem verdauten AST -Formular "kompilieren" und diese "verknüpfen", indem er diese ASTs zu den für den Hauptcode generierten ASTs hinzufügt Dies ermöglicht eine globale Optimierung. Keines der Systeme, die ich verwende, tut dies, daher kann ich nicht beurteilen, wie gut es funktioniert.

Die Möglichkeit, Fragen zur Leistung zu beantworten, besteht darin, immer zu testen (und eine Testumgebung zu verwenden, die der Bereitstellungsumgebung so ähnlich wie möglich ist).

330
dmckee

1) basiert auf der Tatsache, dass das Aufrufen einer DLL) - Funktion immer einen zusätzlichen indirekten Sprung verwendet. Dies ist heutzutage normalerweise vernachlässigbar. Innerhalb der DLL) gibt es Mehr Aufwand für i386-CPUs, da sie keinen positionsunabhängigen Code generieren können.Auf AMD64 können Sprünge relativ zum Programmzähler erfolgen, was eine enorme Verbesserung darstellt.

2) Das ist richtig. Mit Optimierungen, die von der Profilerstellung geleitet werden, können Sie in der Regel eine Leistung von 10 bis 15 Prozent erzielen. Jetzt, da die CPU-Geschwindigkeit an ihre Grenzen gestoßen ist, könnte es sich lohnen, dies zu tun.

Ich würde hinzufügen: (3) Der Linker kann Funktionen in einer Cache-effizienteren Gruppierung anordnen, so dass teure Ausfälle auf Cache-Ebene minimiert werden. Dies kann sich auch besonders auf die Startzeit von Anwendungen auswirken (basierend auf den Ergebnissen, die ich mit dem Sun C++ - Compiler gesehen habe).

Und vergessen Sie nicht, dass mit DLLs keine Beseitigung von totem Code durchgeführt werden kann. Je nach Sprache ist der DLL - Code möglicherweise auch nicht optimal. Virtuelle Funktionen sind immer virtuell, da der Compiler nicht weiß, ob ein Client sie überschreibt.

Aus diesen Gründen, falls es keinen wirklichen Bedarf für DLLs gibt, verwenden Sie einfach die statische Kompilierung.

BEARBEITEN (um den Kommentar zu beantworten, vom Benutzer unterstrichen)

Hier ist eine gute Ressource zum Problem des positionsunabhängigen Codes http://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/

Wie bereits erläutert, verfügt x86 über keine AFAIK für andere als 15-Bit-Sprungbereiche und nicht für bedingungslose Sprünge und Aufrufe. Aus diesem Grund waren Funktionen (von Generatoren) mit mehr als 32 KB immer ein Problem und benötigten eingebettete Trampoline.

Aber unter populären x86-Betriebssystemen wie Linux ist es egal, ob die SO/DLL-Datei nicht mit dem Schalter gcc-fpic Generiert wird (wodurch die Verwendung der indirekten Sprungtabellen erzwungen wird). . Wenn Sie dies nicht tun, wird der Code einfach so repariert, wie ein normaler Linker ihn verlagern würde. Dabei wird das Codesegment jedoch nicht mehr gemeinsam genutzt, und es muss eine vollständige Zuordnung des Codes von der Festplatte in den Speicher vorgenommen und alles berührt werden, bevor er verwendet werden kann (Leeren der meisten Caches, Erreichen von TLBs usw.). Es gab eine Zeit wenn dies als langsam galt ... zu langsam.

Sie würden also keinen Nutzen mehr haben.

Ich kann mich nicht erinnern, unter welchem ​​Betriebssystem (Solaris oder FreeBSD) Probleme mit meinem Unix-Build-System aufgetreten sind, weil ich dies einfach nicht getan habe und mich gefragt habe, warum es abgestürzt ist, bis ich -fPIC Auf gcc angewendet habe.

66
Lothar

Dynamisches Verknüpfen ist die einzige praktische Möglichkeit, um bestimmte Lizenzanforderungen zu erfüllen, z. B. LGPL .

64
Mark Ransom

Ich bin mit den Punkten einverstanden, die dnmckee erwähnt, plus:

  • Statisch verknüpfte Anwendungen sind möglicherweise einfacher bereitzustellen, da weniger oder keine zusätzlichen Dateiabhängigkeiten (DLL/SO) vorhanden sind, die Probleme verursachen können, wenn sie fehlen oder am falschen Ort installiert sind.
43
stakx

Ein Grund für die Ausführung eines statisch verknüpften Builds besteht darin, sicherzustellen, dass die ausführbare Datei vollständig geschlossen ist, d. H., Dass alle Symbolverweise korrekt aufgelöst wurden.

Als Teil eines großen Systems, das mit kontinuierlicher Integration erstellt und getestet wurde, wurden die nächtlichen Regressionstests unter Verwendung einer statisch verknüpften Version der ausführbaren Dateien ausgeführt. Gelegentlich wurde festgestellt, dass ein Symbol nicht aufgelöst wurde und die statische Verknüpfung fehlschlug, obwohl die dynamisch verknüpfte ausführbare Datei erfolgreich verknüpft wurde.

Dies trat normalerweise auf, wenn Symbole, die sich tief in den gemeinsam genutzten Bibliotheken befanden, einen falschen Namen hatten und daher nicht statisch verknüpft waren. Der dynamische Linker löst nicht alle Symbole vollständig auf, unabhängig von der Tiefen- oder Breitenauswertung. Sie können also eine dynamisch verknüpfte ausführbare Datei erstellen, die nicht vollständig geschlossen ist.

32
Rob Wells

1/Ich war an Projekten beteiligt, bei denen dynamisches und statisches Verknüpfen verglichen wurden und der Unterschied nicht klein genug war, um auf dynamisches Verknüpfen umzuschalten (ich war nicht Teil des Tests, ich kenne nur die Schlussfolgerung).

2/Dynamisches Verknüpfen wird häufig mit PIC (Position Independent Code, Code, der abhängig von der Adresse, unter der er geladen wird, nicht geändert werden muss) assoziiert. Abhängig von der Architektur kann PIC zu einer weiteren Verlangsamung führen, ist jedoch erforderlich, um eine dynamisch verknüpfte Bibliothek zwischen zwei ausführbaren Dateien (und sogar zwei Prozessen derselben ausführbaren Datei, wenn das Betriebssystem die Zufallsgenerierung der Ladeadresse als Sicherheitsmaßnahme verwendet) gemeinsam zu nutzen. Ich bin nicht sicher, ob alle Betriebssysteme die Trennung der beiden Konzepte zulassen, aber Solaris und Linux sowie ISTR, die HP-UX ebenfalls unterstützt.

3/Ich habe an anderen Projekten mitgewirkt, bei denen dynamische Verknüpfungen für die Funktion "easy patch" verwendet wurden. Aber dieser "einfache Patch" macht das Verteilen von kleinen Fixes ein wenig einfacher und von komplizierten zu einem versionierenden Albtraum. Wir mussten oft alles pushen und Probleme beim Kunden nachverfolgen, weil die falsche Version das Token war.

Meine Schlussfolgerung ist, dass ich statische Verknüpfungen verwendet habe, mit Ausnahme von:

  • für Dinge wie Plugins, die auf dynamisches Verknüpfen angewiesen sind

  • wenn das Teilen wichtig ist (große Bibliotheken, die von mehreren Prozessen gleichzeitig verwendet werden, wie C/C++ - Laufzeit, GUI-Bibliotheken usw., die häufig unabhängig voneinander verwaltet werden und für die die ABI streng definiert ist)

Wenn man den "einfachen Patch" verwenden möchte, würde ich argumentieren, dass die Bibliotheken wie die großen Bibliotheken oben verwaltet werden müssen: Sie müssen mit einem definierten ABI nahezu unabhängig sein, das nicht durch Fixes geändert werden darf.

21
AProgrammer

This ausführlich über gemeinsam genutzte Bibliotheken unter Linux und die Auswirkungen auf die Leistung diskutieren.

20
nos

Es ist wirklich ziemlich einfach. Wenn Sie eine Änderung an Ihrem Quellcode vornehmen, möchten Sie 10 Minuten oder 20 Sekunden warten, bis der Quellcode erstellt wurde? 20 Sekunden ist alles, was ich ertragen kann. Darüber hinaus hole ich entweder das Schwert heraus oder beginne darüber nachzudenken, wie ich es mithilfe einer separaten Zusammenstellung und Verknüpfung wieder in die Komfortzone zurückbringen kann.

11
Hans Passant

Auf Unix-ähnlichen Systemen kann die dynamische Verknüpfung die Verwendung einer Anwendung durch 'root' erschweren, wenn die gemeinsam genutzten Bibliotheken an einem abgelegenen Ort installiert sind. Dies liegt daran, dass der dynamische Linker im Allgemeinen LD_LIBRARY_PATH oder dessen Entsprechung für Prozesse mit Root-Rechten nicht berücksichtigt. Manchmal spart dann statische Verknüpfung den Tag.

Alternativ muss der Installationsprozess die Bibliotheken lokalisieren. Dies kann jedoch die Koexistenz mehrerer Versionen der Software auf dem Computer erschweren.

10

Das dynamische Verknüpfen erfordert zusätzliche Zeit, damit das Betriebssystem die dynamische Bibliothek findet und lädt. Mit statischer Verknüpfung ist alles zusammen und es ist eine einmalige Ladung in den Speicher.

Siehe auch DLL Hell . In diesem Szenario ist die vom Betriebssystem geladene DLL nicht diejenige, die mit Ihrer Anwendung geliefert wurde, oder die Version, die Ihre Anwendung erwartet.

7
Thomas Matthews

Das beste Beispiel für eine dynamische Verknüpfung ist, wenn die Bibliothek von der verwendeten Hardware abhängig ist. In früheren Zeiten wurde die C-Mathematikbibliothek als dynamisch eingestuft, sodass jede Plattform alle Prozessorfunktionen nutzen kann, um sie zu optimieren.

Ein noch besseres Beispiel könnte OpenGL sein. OpenGl ist eine API, die von AMD und NVidia unterschiedlich implementiert wird. Und Sie können eine NVidia-Implementierung auf einer AMD-Karte nicht verwenden, da die Hardware unterschiedlich ist. Sie können OpenGL daher nicht statisch in Ihr Programm einbinden. Die dynamische Verknüpfung wird hier verwendet, um die API für alle Plattformen zu optimieren.

7
Arne

Ein weiteres Problem, das noch nicht behandelt wurde, ist die Behebung von Fehlern in der Bibliothek.

Mit der statischen Verknüpfung müssen Sie nicht nur die Bibliothek neu erstellen, sondern auch die ausführbare Datei neu verknüpfen und verteilen. Wenn die Bibliothek nur in einer ausführbaren Datei verwendet wird, ist dies möglicherweise kein Problem. Aber je mehr ausführbare Dateien neu verknüpft und verteilt werden müssen, desto größer ist der Schmerz.

Mit der dynamischen Verknüpfung können Sie die dynamische Bibliothek einfach neu erstellen und verteilen, und schon sind Sie fertig.

5

durch statisches Verknüpfen erhalten Sie nur eine einzige Exe, um eine Änderung vorzunehmen, die Sie zum erneuten Kompilieren Ihres gesamten Programms benötigen. Während Sie bei der dynamischen Verknüpfung nur Änderungen an der DLL vornehmen müssen und wenn Sie Ihre Exe ausführen, werden die Änderungen zur Laufzeit übernommen. Aktualisierungen und Fehlerbehebungen lassen sich durch die dynamische Verknüpfung leichter vornehmen (z. B. Windows).

3

Es gibt eine große und zunehmende Anzahl von Systemen, bei denen eine extreme statische Verknüpfung enorme positive Auswirkungen auf die Anwendungen und die Systemleistung haben kann.

Ich beziehe mich auf sogenannte "eingebettete Systeme", von denen viele zunehmend Allzweck-Betriebssysteme verwenden und diese Systeme für alles Mögliche verwendet werden.

Ein sehr verbreitetes Beispiel sind Geräte, die GNU/Linux-Systeme mit Busybox verwenden. Ich habe dies mit NetBSD auf die Spitze getrieben, indem ich ein bootfähiges i386 (32-Bit) -System-Image erstellt habe, das sowohl einen Kernel als auch sein Root-Dateisystem enthält crunchgen) Binärdatei mit Verknüpfungen zu allen Programmen, die selbst alle (nun endlich 274) der Standard-Vollversion enthalten Systemprogramme (die meisten außer der Toolchain), und es ist weniger als 20 Megabyte groß (und läuft wahrscheinlich sehr komfortabel in einem System mit nur 64 MB Arbeitsspeicher (auch wenn das Root-Dateisystem nicht komprimiert und vollständig im RAM gespeichert ist), obwohl ich keinen gefunden habe, der so klein ist, dass ich ihn testen könnte).

In früheren Beiträgen wurde erwähnt, dass die Startzeit von statisch verknüpften Binärdateien kürzer ist (und es kann ein lot kürzer sein), aber das ist nur ein Teil des Bildes, insbesondere Wenn der gesamte Objektcode in der gleichen Datei verknüpft ist, und insbesondere, wenn das Betriebssystem die Anforderung unterstützt, Code direkt aus der ausführbaren Datei auszulagern. In diesem idealen Szenario ist die Startzeit von Programmen im wahrsten Sinne des Wortes vernachlässigbar, da sich fast alle Codeseiten bereits im Speicher befinden und von der Shell (und init) verwendet werden Hintergrundprozesse, die möglicherweise ausgeführt werden), auch wenn das angeforderte Programm seit dem Startvorgang noch nie ausgeführt wurde, da möglicherweise nur eine Seite Speicher geladen werden muss, um die Laufzeitanforderungen des Programms zu erfüllen.

Das ist jedoch noch nicht die ganze Geschichte. Normalerweise erstelle und verwende ich auch die NetBSD-Betriebssysteminstallationen für meine vollständigen Entwicklungssysteme, indem ich alle Binärdateien statisch verbinde. Auch wenn dies eine enorme Menge an Speicherplatz in Anspruch nimmt (insgesamt ~ 6,6 GB für x86_64 mit allem, einschließlich Toolchain und X11 mit statischen Verknüpfungen) (insbesondere, wenn vollständige Debug-Symboltabellen für alle Programme verfügbar bleiben (weitere ~ 2,5 GB), bleibt das Ergebnis dennoch bestehen Läuft insgesamt schneller und benötigt für einige Aufgaben sogar weniger Speicher als ein typisches System mit dynamischer Verknüpfung, das vorgibt, Bibliothekscodeseiten gemeinsam zu nutzen. Festplatte ist billig (sogar schnelle Festplatte), und Arbeitsspeicher zum Zwischenspeichern häufig verwendeter Festplattendateien ist ebenfalls relativ billig, aber CPU-Zyklen sind es wirklich nicht, und die ld.so - Startkosten werden für jeden Prozess gezahlt, der every startet Die Startzeit nimmt Stunden und Stunden von CPU-Zyklen in Anspruch, bevor Aufgaben ausgeführt werden müssen, bei denen viele Prozesse gestartet werden müssen, insbesondere wenn dieselben Programme immer wieder verwendet werden, z. B. Compiler auf einem Entwicklungssystem. Statisch verknüpfte Toolchain-Programme können die Build-Zeiten für mehrere Architekturen meines Systems um Stunden verkürzen. Ich habe die Toolchain noch nicht in meine einzelne crunchgen ed-Binärdatei eingebaut, aber ich vermute, dass aufgrund des Gewinns für den CPU-Cache mehr Stunden Bauzeit eingespart werden.

2
Greg A. Woods

Die statische Verknüpfung umfasst die Dateien, die das Programm benötigt, in einer einzigen ausführbaren Datei.

Dynamische Verknüpfungen sind das, was Sie für gewöhnlich halten würden. Sie machen eine ausführbare Datei, für die weiterhin DLLs erforderlich sind und die sich im selben Verzeichnis befinden (oder die DLLs könnten sich im Systemordner befinden).

(DLL = dynamischer Link Bibliothek)

Dynamisch verknüpfte ausführbare Dateien werden schneller kompiliert und sind nicht so ressourcenintensiv.

1
Nykal