490 Stimmen

Statische Verknüpfung vs. dynamische Verknüpfung

Gibt es zwingende Leistungsgründe, die in bestimmten Situationen dafür sprechen, statisches Linking dem dynamischen Linking vorzuziehen oder umgekehrt? Ich habe Folgendes gehört oder gelesen, aber ich weiß nicht genug über das Thema, um mich für seine Richtigkeit zu verbürgen.

1) Der Unterschied in der Laufzeitleistung zwischen statischem Linking und dynamischem Linking ist in der Regel vernachlässigbar.

2) (1) trifft nicht zu, wenn ein profilierender Compiler verwendet wird, der Profildaten zur Optimierung von Programm-Hotpaths verwendet, da der Compiler beim statischen Linken sowohl Ihren Code als auch den Code der Bibliothek optimieren kann. Beim dynamischen Linking kann nur Ihr Code optimiert werden. Wenn die meiste Zeit mit der Ausführung von Bibliothekscode verbracht wird, kann dies einen großen Unterschied ausmachen. Ansonsten gilt immer noch (1).

422voto

  • Dynamisch Verlinkung kann den gesamten Ressourcenverbrauch reduzieren (wenn mehr als ein Prozess dieselbe Bibliothek nutzt (einschließlich der Version in "dieselbe", natürlich)). Ich glaube, das ist das Argument, das in den meisten Umgebungen dafür spricht. Zu den "Ressourcen" gehören hier Festplattenplatz, RAM und Cache-Speicher. Wenn Ihr dynamischer Linker nicht flexibel genug ist, besteht natürlich die Gefahr, dass DLL-Hölle .
  • Dynamisch Verknüpfung bedeutet, dass Fehlerbehebungen und Aktualisierungen von Bibliotheken verbreiten. zu verbessern Ihr Produkt, ohne dass Sie etwas versenden müssen.
  • Plugins immer verlangen dynamisch Verknüpfung.
  • Statisch Verknüpfung, bedeutet, dass Sie wissen können, dass der Code in sehr begrenzte Umgebungen (zu Beginn des Bootvorgangs oder im Rettungsmodus).
  • Statisch Verknüpfung kann Binärdateien erzeugen leichter zu vertreiben für verschiedene Benutzerumgebungen (um den Preis, dass ein größeres und ressourcenintensiveres Programm gesendet wird).
  • Statisch Die Verknüpfung kann eine leichte schnellere Inbetriebnahme Zeiten, aber das hängt bis zu einem gewissen Grad von der Größe und Komplexität Ihres Programms ab et über die Einzelheiten der Ladestrategie des Betriebssystems.

Einige Änderungen, um die sehr relevanten Vorschläge in den Kommentaren und in anderen Antworten aufzunehmen. Ich möchte anmerken, dass die Art und Weise, wie Sie das Problem angehen, stark von der Umgebung abhängt, in der Sie arbeiten wollen. Kleine eingebettete Systeme haben möglicherweise nicht genug Ressourcen, um dynamisches Linking zu unterstützen. Etwas größere kleine Systeme können durchaus dynamisches Linking unterstützen, da ihr Speicher klein genug ist, um die RAM-Einsparungen durch dynamisches Linking sehr attraktiv zu machen. Ausgereifte Consumer-PCs haben, wie Notizen Sie haben enorme Ressourcen, und Sie können sich in dieser Angelegenheit wahrscheinlich von Bequemlichkeitsaspekten leiten lassen.


Behebung der Leistungs- und Effizienzprobleme: es kommt darauf an .

Klassischerweise erfordern dynamische Bibliotheken eine Art Klebeschicht, was oft eine doppelte Abfertigung oder eine zusätzliche Schicht von Indirektion in der Funktionsadressierung bedeutet und ein wenig Geschwindigkeit kosten kann (aber ist die Funktionsaufrufzeit tatsächlich ein großer Teil Ihrer Laufzeit???).

Wenn Sie jedoch mehrere Prozesse laufen lassen, die alle häufig dieselbe Bibliothek aufrufen, können Sie bei der Verwendung von dynamischem Linking im Vergleich zum statischen Linking Cache-Zeilen einsparen (und damit an Laufleistung gewinnen). (Es sei denn, moderne Betriebssysteme sind intelligent genug, um identische Segmente in statisch gelinkten Binärdateien zu erkennen. Scheint schwer zu sein, weiß das jemand?)

Ein weiteres Problem: die Ladezeit. Irgendwann zahlen Sie Ladekosten. Wann Sie diese Kosten zahlen, hängt davon ab, wie das Betriebssystem funktioniert und welche Verknüpfungen Sie verwenden. Vielleicht möchten Sie die Kosten lieber aufschieben, bis Sie wissen, dass Sie sie brauchen.

Beachten Sie, dass die statische-vs-dynamische Verknüpfung traditionell pas ein Optimierungsproblem, da beide eine separate Kompilierung in Objektdateien erfordern. Dies ist jedoch nicht erforderlich: Ein Compiler kann im Prinzip "statische Bibliotheken" zunächst in eine verdaute AST-Form "kompilieren" und sie dann "verlinken", indem er diese ASTs zu denen hinzufügt, die für den Hauptcode erzeugt werden, wodurch eine globale Optimierung ermöglicht wird. Keines der Systeme, die ich verwende, tut dies, also kann ich nicht sagen, wie gut es funktioniert.

Fragen zur Leistung lassen sich wie folgt beantworten immer durch Testen (und verwenden Sie eine Testumgebung, die der Einsatzumgebung so weit wie möglich entspricht).

78voto

Lothar Punkte 11643

1) basiert auf der Tatsache, dass der Aufruf einer DLL-Funktion immer einen zusätzlichen indirekten Sprung erfordert. Heutzutage ist dies normalerweise vernachlässigbar. Innerhalb der DLL gibt es auf i386-CPUs etwas mehr Overhead, weil sie keinen positionsunabhängigen Code erzeugen können. Auf amd64 können Sprünge relativ zum Programmzähler erfolgen, was eine große Verbesserung darstellt.

2) Das ist richtig. Mit Optimierungen, die von der Profilerstellung geleitet werden, kann man in der Regel etwa 10-15 Prozent Leistung gewinnen. Jetzt, wo die CPU-Geschwindigkeit ihre Grenzen erreicht hat, 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 Cache-Level-Misses minimiert werden. Dies könnte 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 kein toter Code beseitigt werden kann. Je nach Sprache kann der DLL-Code auch nicht optimal sein. Virtuelle Funktionen sind immer virtuell, da der Compiler nicht weiß, ob sie von einem Client überschrieben werden.

Aus diesen Gründen sollte man, wenn kein wirklicher Bedarf an DLLs besteht, nur die statische Kompilierung verwenden.

EDIT (zur Beantwortung des Kommentars, durch Unterstreichen des Benutzers)

Hier finden Sie eine gute Quelle für das Problem des positionsunabhängigen Codes http://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/

Wie bereits erwähnt, gibt es sie bei x86 nur für 15-Bit-Sprungbereiche und nicht für unbedingte Sprünge und Aufrufe. Deshalb waren Funktionen (von Generatoren) mit mehr als 32K immer ein Problem und benötigten eingebettete Trampoline.

Aber auf gängigen x86-Betriebssystemen wie Linux müssen Sie sich nicht darum kümmern, ob die .so/DLL-Datei nicht mit dem gcc Schalter -fpic (was die Verwendung der indirekten Sprungtabellen erzwingt). Denn wenn Sie das nicht tun, wird der Code einfach so fixiert, wie ein normaler Linker ihn verschieben würde. Dadurch wird das Codesegment jedoch nicht gemeinsam nutzbar, und es wäre eine vollständige Abbildung des Codes von der Festplatte in den Speicher erforderlich, bevor er verwendet werden kann (Leeren der meisten Caches, Angreifen der TLBs) usw. Es gab eine Zeit, in der dies als langsam galt.

Sie würden also keinen Nutzen mehr haben.

Ich kann mich nicht daran erinnern, welches Betriebssystem (Solaris oder FreeBSD) mir Probleme mit meinem Unix-Build-System bereitet hat, denn ich habe das einfach nicht gemacht und mich gewundert, warum es abgestürzt ist, bis ich die -fPIC a gcc .

72voto

Mark Ransom Punkte 283960

Die dynamische Verknüpfung ist die einzige praktische Möglichkeit, einige Lizenzanforderungen zu erfüllen, wie z. B. die LGPL .

47voto

Ich stimme mit den von dnmckee genannten Punkten überein:

  • Statisch gelinkte Anwendungen sind möglicherweise einfacher zu implementieren, da es weniger oder keine zusätzlichen Dateiabhängigkeiten (.dll / .so) gibt, die Probleme verursachen könnten, wenn sie fehlen oder an der falschen Stelle installiert werden.

35voto

Rob Wells Punkte 35303

Ein Grund, einen statisch gelinkten Build durchzuführen, ist die Überprüfung, ob die ausführbare Datei vollständig geschlossen ist, d.h. ob alle Symbolreferenzen korrekt aufgelöst sind.

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

Dies trat in der Regel auf, wenn Symbole, die tief in den gemeinsam genutzten Bibliotheken verankert waren, einen falsch geschriebenen Namen hatten und daher nicht statisch verknüpft werden konnten. Der dynamische Linker löst nicht alle Symbole vollständig auf, unabhängig davon, ob er eine Deep-First- oder eine Breadth-First-Evaluierung verwendet, so dass eine dynamisch gelinkte ausführbare Datei entstehen kann, die nicht vollständig geschlossen ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X