Das Kompilieren einer C ++ - Datei dauert im Vergleich zu C # und Java sehr lange. Das Kompilieren einer C ++ - Datei dauert erheblich länger als das Ausführen eines Python-Skripts normaler Größe. Ich verwende derzeit VC ++, aber es ist das gleiche mit jedem Compiler. Warum ist das?
Die beiden Gründe, an die ich denken konnte, waren das Laden von Header-Dateien und das Ausführen des Präprozessors, aber das scheint nicht zu erklären, warum es so lange dauert.
c++
performance
compilation
Dan Goldstein
quelle
quelle
It takes significantly longer to compile a C++ file
- Meinst du 2 Sekunden im Vergleich zu 1 Sekunde? Das ist zwar doppelt so lang, aber kaum von Bedeutung. Oder meinst du 10 Minuten im Vergleich zu 5 Sekunden? Bitte quantifizieren.Antworten:
Mehrere Gründe
Header-Dateien
Für jede einzelne Kompilierungseinheit müssen Hunderte oder sogar Tausende von Headern (1) geladen und (2) kompiliert werden. Jeder von ihnen hat in der Regel für jede Übersetzungseinheit neu kompiliert werden, da die Prä - Prozessor sorgt dafür , dass das Ergebnis eine Kopfzusammenstellung kann zwischen jeder Übersetzungseinheit variieren. (Ein Makro kann in einer Kompilierungseinheit definiert werden, die den Inhalt des Headers ändert.)
Dies ist wahrscheinlich der Hauptgrund, da für jede Kompilierungseinheit große Mengen an Code kompiliert werden müssen und außerdem jeder Header mehrmals kompiliert werden muss (einmal für jede Kompilierungseinheit, die ihn enthält).
Verknüpfen
Nach dem Kompilieren müssen alle Objektdateien miteinander verknüpft werden. Dies ist im Grunde ein monolithischer Prozess, der nicht sehr gut parallelisiert werden kann und Ihr gesamtes Projekt verarbeiten muss.
Parsing
Die Syntax ist äußerst kompliziert zu analysieren, hängt stark vom Kontext ab und ist sehr schwer zu unterscheiden. Das braucht viel Zeit.
Vorlagen
In C #
List<T>
ist der einzige Typ, der kompiliert wird, unabhängig davon, wie viele Instanzen von List Sie in Ihrem Programm haben. In C ++vector<int>
ist ein völlig separater Typ vonvector<float>
, und jeder muss separat kompiliert werden.Hinzu kommt, dass Vorlagen eine vollständige Turing-vollständige "Subsprache" bilden, die der Compiler interpretieren muss, und dies kann lächerlich kompliziert werden. Selbst relativ einfacher Metaprogrammiercode für Vorlagen kann rekursive Vorlagen definieren, die Dutzende und Dutzende von Vorlageninstanziierungen erstellen. Vorlagen können auch zu äußerst komplexen Typen mit lächerlich langen Namen führen, die dem Linker viel zusätzliche Arbeit hinzufügen. (Es müssen viele Symbolnamen verglichen werden, und wenn diese Namen zu vielen tausend Zeichen werden können, kann dies ziemlich teuer werden.)
Und natürlich verschärfen sie die Probleme mit Header-Dateien, da Vorlagen im Allgemeinen in Headern definiert werden müssen, was bedeutet, dass für jede Kompilierungseinheit viel mehr Code analysiert und kompiliert werden muss. Im einfachen C-Code enthält ein Header normalerweise nur Vorwärtsdeklarationen, aber nur sehr wenig tatsächlichen Code. In C ++ ist es nicht ungewöhnlich, dass sich fast der gesamte Code in Header-Dateien befindet.
Optimierung
C ++ ermöglicht einige sehr dramatische Optimierungen. Mit C # oder Java können Klassen nicht vollständig eliminiert werden (sie müssen zu Reflexionszwecken vorhanden sein), aber selbst ein einfaches C ++ - Vorlagen-Metaprogramm kann leicht Dutzende oder Hunderte von Klassen generieren, die alle in der Optimierung inline und wieder eliminiert werden Phase.
Darüber hinaus muss ein C ++ - Programm vom Compiler vollständig optimiert werden. Das AC # -Programm kann sich darauf verlassen, dass der JIT-Compiler beim Laden zusätzliche Optimierungen durchführt. C ++ erhält keine solchen "zweiten Chancen". Was der Compiler generiert, ist so optimiert wie es nur geht.
Maschine
C ++ wird zu Maschinencode kompiliert, der möglicherweise etwas komplizierter ist als der von Java oder .NET verwendete Bytecode (insbesondere im Fall von x86). (Dies wird nur der Vollständigkeit halber erwähnt, weil es in Kommentaren und dergleichen erwähnt wurde. In der Praxis ist es unwahrscheinlich, dass dieser Schritt mehr als einen winzigen Bruchteil der gesamten Kompilierungszeit in Anspruch nimmt.)
Fazit
Die meisten dieser Faktoren werden von C-Code gemeinsam genutzt, der tatsächlich ziemlich effizient kompiliert wird. Der Parsing-Schritt ist in C ++ viel komplizierter und kann erheblich mehr Zeit in Anspruch nehmen, aber der Haupttäter sind wahrscheinlich Vorlagen. Sie sind nützlich und machen C ++ zu einer weitaus leistungsfähigeren Sprache, aber sie fordern auch ihren Tribut in Bezug auf die Kompilierungsgeschwindigkeit.
quelle
Die Verlangsamung ist bei keinem Compiler unbedingt gleich.
Ich habe weder Delphi noch Kylix verwendet, aber in den MS-DOS-Tagen wurde ein Turbo Pascal-Programm fast sofort kompiliert, während das entsprechende Turbo C ++ - Programm nur crawlt.
Die beiden Hauptunterschiede waren ein sehr starkes Modulsystem und eine Syntax, die eine Kompilierung in einem Durchgang ermöglichte.
Es ist sicher möglich, dass die Kompilierungsgeschwindigkeit für C ++ - Compilerentwickler keine Priorität hatte, aber es gibt auch einige inhärente Komplikationen in der C / C ++ - Syntax, die die Verarbeitung erschweren. (Ich bin kein Experte für C, aber Walter Bright ist es. Nachdem er verschiedene kommerzielle C / C ++ - Compiler erstellt hat, hat er die D-Sprache erstellt. Eine seiner Änderungen bestand darin, eine kontextfreie Grammatik zu erzwingen, um das Parsen der Sprache zu vereinfachen .)
Außerdem werden Sie feststellen, dass Makefiles im Allgemeinen so eingerichtet sind, dass jede Datei separat in C kompiliert wird. Wenn also 10 Quelldateien dieselbe Include-Datei verwenden, wird diese Include-Datei zehnmal verarbeitet.
quelle
Parsing und Codegenerierung sind eigentlich ziemlich schnell. Das eigentliche Problem ist das Öffnen und Schließen von Dateien. Denken Sie daran, dass der Compiler auch mit Include-Wachen die .H-Datei geöffnet und jede Zeile gelesen hat (und sie dann ignoriert).
Ein Freund nahm einmal (während er sich bei der Arbeit langweilte) die Bewerbung seines Unternehmens und legte alles - alle Quell- und Header-Dateien - in eine große Datei. Die Kompilierungszeit wurde von 3 Stunden auf 7 Minuten gesenkt.
quelle
Ein weiterer Grund ist die Verwendung des C-Vorprozessors zum Auffinden von Deklarationen. Selbst mit Header-Wachen muss .h jedes Mal, wenn sie enthalten sind, immer wieder analysiert werden. Einige Compiler unterstützen vorkompilierte Header, die dabei helfen können, werden jedoch nicht immer verwendet.
Siehe auch: Häufig gestellte C ++ - Antworten
quelle
C ++ wird in Maschinencode kompiliert. Sie haben also den Vorprozessor, den Compiler, den Optimierer und schließlich den Assembler, die alle ausgeführt werden müssen.
Java und C # werden vor der Ausführung in Bytecode / IL kompiliert, und die Java Virtual Machine / .NET Framework wird vor der Ausführung ausgeführt (oder JIT wird in Maschinencode kompiliert).
Python ist eine interpretierte Sprache, die auch in Bytecode kompiliert wird.
Ich bin mir sicher, dass es auch andere Gründe dafür gibt, aber im Allgemeinen spart es Zeit, nicht in die native Maschinensprache kompilieren zu müssen.
quelle
Die größten Probleme sind:
1) Die unendliche Header-Wiederholung. Schon erwähnt. Schadensbegrenzungen (wie #pragma einmal) funktionieren normalerweise nur pro Kompilierungseinheit, nicht pro Build.
2) Die Tatsache, dass die Toolchain häufig in mehrere Binärdateien unterteilt ist (make, Präprozessor, Compiler, Assembler, Archiver, impdef, Linker und dlltool im Extremfall), die alle bei jedem Aufruf den Status neu initialisieren und neu laden müssen ( Compiler, Assembler) oder alle paar Dateien (Archivierer, Linker und DLL-Tool).
Siehe auch diese Diskussion zu comp.compilers: http://compilers.iecc.com/comparch/article/03-11-078 speziell diese:
http://compilers.iecc.com/comparch/article/02-07-128
Beachten Sie, dass John, der Moderator von comp.compilers, zuzustimmen scheint und dass dies bedeutet, dass es möglich sein sollte, ähnliche Geschwindigkeiten auch für C zu erreichen, wenn man die Toolchain vollständig integriert und vorkompilierte Header implementiert. Viele kommerzielle C-Compiler tun dies bis zu einem gewissen Grad.
Beachten Sie, dass das Unix-Modell, bei dem alles in eine separate Binärdatei zerlegt wird, eine Art Worst-Case-Modell für Windows ist (mit seiner langsamen Prozesserstellung). Dies ist beim Vergleich der GCC-Erstellungszeiten zwischen Windows und * nix sehr auffällig, insbesondere wenn das make / configure-System auch einige Programme aufruft, um Informationen zu erhalten.
quelle
Erstellen von C / C ++: Was passiert wirklich und warum dauert es so lange?
Ein relativ großer Teil der Softwareentwicklungszeit wird nicht für das Schreiben, Ausführen, Debuggen oder sogar Entwerfen von Code aufgewendet, sondern darauf gewartet, dass der Kompilierungsvorgang abgeschlossen ist. Um die Dinge schnell zu machen, müssen wir zuerst verstehen, was passiert, wenn C / C ++ - Software kompiliert wird. Die Schritte sind ungefähr wie folgt:
Wir werden uns nun jeden Schritt genauer ansehen und uns darauf konzentrieren, wie sie schneller gemacht werden können.
Aufbau
Dies ist der erste Schritt beim Erstellen. Normalerweise bedeutet dies, ein Konfigurationsskript oder CMake, Gyp, SCons oder ein anderes Tool auszuführen. Dies kann bei sehr großen Autotools-basierten Konfigurationsskripten zwischen einer Sekunde und mehreren Minuten dauern.
Dieser Schritt kommt relativ selten vor. Es muss nur ausgeführt werden, wenn Konfigurationen oder Build-Konfigurationen geändert werden. Ohne die Änderung der Build-Systeme muss nicht viel getan werden, um diesen Schritt zu beschleunigen.
Tool starten
Dies passiert, wenn Sie make ausführen oder auf das Build-Symbol einer IDE klicken (was normalerweise ein Alias für make ist). Die Binärdatei des Build-Tools startet und liest die Konfigurationsdateien sowie die Build-Konfiguration, die normalerweise identisch sind.
Je nach Komplexität und Größe des Builds kann dies zwischen einem Bruchteil einer Sekunde und mehreren Sekunden dauern. An sich wäre das nicht so schlimm. Leider führen die meisten make-basierten Build-Systeme dazu, dass make für jeden einzelnen Build zehn- bis hundertmal aufgerufen wird. Normalerweise wird dies durch die rekursive Verwendung von make verursacht (was schlecht ist).
Es sollte beachtet werden, dass der Grund, warum Make so langsam ist, kein Implementierungsfehler ist. Die Syntax von Makefiles hat einige Macken, die eine wirklich schnelle Implementierung so gut wie unmöglich machen. Dieses Problem macht sich in Kombination mit dem nächsten Schritt noch deutlicher bemerkbar.
Abhängigkeitsprüfung
Sobald das Build-Tool seine Konfiguration gelesen hat, muss es feststellen, welche Dateien geändert wurden und welche neu kompiliert werden müssen. Die Konfigurationsdateien enthalten ein gerichtetes azyklisches Diagramm, das die Build-Abhängigkeiten beschreibt. Dieses Diagramm wird normalerweise während des Konfigurationsschritts erstellt. Die Startzeit des Build-Tools und der Abhängigkeitsscanner werden bei jedem einzelnen Build ausgeführt. Ihre kombinierte Laufzeit bestimmt die Untergrenze des Edit-Compile-Debug-Zyklus. Bei kleinen Projekten beträgt diese Zeit normalerweise einige Sekunden. Das ist erträglich. Es gibt Alternativen zu Make. Der schnellste von ihnen ist Ninja, der von Google-Ingenieuren für Chromium entwickelt wurde. Wenn Sie CMake oder Gyp zum Erstellen verwenden, wechseln Sie einfach zu den Ninja-Backends. Sie müssen nichts an den Build-Dateien selbst ändern, sondern nur den Geschwindigkeitsschub genießen. Ninja ist jedoch auf den meisten Distributionen nicht enthalten.
Zusammenstellung
An dieser Stelle rufen wir endlich den Compiler auf. Hier sind die ungefähren Schritte aufgeführt.
Entgegen der landläufigen Meinung ist das Kompilieren von C ++ nicht allzu langsam. Die STL ist langsam und die meisten Build-Tools zum Kompilieren von C ++ sind langsam. Es gibt jedoch schnellere Tools und Möglichkeiten, um die langsamen Teile der Sprache zu mildern.
Die Verwendung erfordert etwas Ellbogenfett, aber die Vorteile sind unbestreitbar. Schnellere Erstellungszeiten führen zu glücklicheren Entwicklern, mehr Flexibilität und schließlich zu besserem Code.
quelle
Eine kompilierte Sprache erfordert immer einen höheren anfänglichen Aufwand als eine interpretierte Sprache. Außerdem haben Sie Ihren C ++ - Code möglicherweise nicht sehr gut strukturiert. Zum Beispiel:
Kompiliert viel langsamer als:
quelle
Eine einfache Möglichkeit, die Kompilierungszeit in größeren C ++ - Projekten zu verkürzen, besteht darin, eine * .cpp-Include-Datei zu erstellen, die alle cpp-Dateien in Ihrem Projekt enthält, und diese zu kompilieren. Dies reduziert das Problem der Header-Explosion auf einmal. Dies hat den Vorteil, dass Kompilierungsfehler weiterhin auf die richtige Datei verweisen.
Angenommen, Sie haben a.cpp, b.cpp und c.cpp. Erstellen Sie eine Datei: Everything.cpp:
Kompilieren Sie dann das Projekt, indem Sie einfach alles erstellen
quelle
Einige Gründe sind:
1) Die C ++ - Grammatik ist komplexer als C # oder Java und benötigt mehr Zeit zum Parsen.
2) (Wichtiger) Der C ++ - Compiler erzeugt Maschinencode und führt alle Optimierungen während der Kompilierung durch. C # und Java gehen nur den halben Weg und überlassen diese Schritte JIT.
quelle
Der Nachteil ist, dass das Programm etwas schneller läuft. Das mag für Sie während der Entwicklung ein kalter Trost sein, aber es kann sehr wichtig sein, wenn die Entwicklung abgeschlossen ist und das Programm nur von Benutzern ausgeführt wird.
quelle
Die meisten Antworten sind etwas unklar, wenn erwähnt wird, dass C # aufgrund der Kosten für die Ausführung von Aktionen, die in C ++ nur einmal zur Kompilierungszeit ausgeführt werden, immer langsamer ausgeführt wird. Diese Leistungskosten werden auch durch Laufzeitabhängigkeiten beeinflusst (mehr Dinge müssen geladen werden, um in der Lage zu sein zu laufen), ganz zu schweigen davon, dass C # -Programme immer einen höheren Speicherbedarf haben, was dazu führt, dass die Leistung enger mit der Fähigkeit der verfügbaren Hardware zusammenhängt. Gleiches gilt für andere Sprachen, die interpretiert werden oder von einer VM abhängen.
quelle
Ich kann mir zwei Probleme vorstellen, die sich auf die Geschwindigkeit auswirken können, mit der Ihre Programme in C ++ kompiliert werden.
MÖGLICHE AUSGABE 1 - KOMPILIEREN DES HEADERS: (Dies wurde möglicherweise bereits durch eine andere Antwort oder einen anderen Kommentar behoben .) Microsoft Visual C ++ (AKA VC ++) unterstützt vorkompilierte Header, die ich sehr empfehle. Wenn Sie ein neues Projekt erstellen und den von Ihnen erstellten Programmtyp auswählen, sollte ein Fenster des Setup-Assistenten auf Ihrem Bildschirm angezeigt werden. Wenn Sie unten auf die Schaltfläche "Weiter>" klicken, gelangen Sie im Fenster zu einer Seite mit mehreren Funktionslisten. Stellen Sie sicher, dass das Kontrollkästchen neben der Option "Vorkompilierter Header" aktiviert ist. (HINWEIS: Dies war meine Erfahrung mit Win32-Konsolenanwendungen in C ++, dies ist jedoch möglicherweise nicht bei allen Arten von Programmen in C ++ der Fall.)
MÖGLICHE AUSGABE 2 - DER ORT, AN DEN ERSTELLT WIRD: In diesem Sommer nahm ich an einem Programmierkurs teil und wir mussten alle unsere Projekte auf 8-GB-Flash-Laufwerken speichern, da die Computer im Labor, das wir verwendeten, jede Nacht um Mitternacht gelöscht wurden. das hätte alle unsere Arbeit gelöscht. Wenn Sie aus Gründen der Portabilität / Sicherheit / etc. Auf ein externes Speichergerät kompilieren, kann dies sehr lange dauernZeit (auch mit den oben erwähnten vorkompilierten Headern) für die Kompilierung Ihres Programms, insbesondere wenn es sich um ein ziemlich großes Programm handelt. In diesem Fall würde ich Ihnen raten, Programme auf der Festplatte des von Ihnen verwendeten Computers zu erstellen und zu kompilieren. Wenn Sie aus irgendeinem Grund die Arbeit an Ihren Projekten einstellen möchten / müssen, übertragen Sie sie auf Ihre externe Klicken Sie auf das Symbol „Hardware sicher entfernen und Medien auswerfen“, das als kleines Flash-Laufwerk hinter einem kleinen grünen Kreis mit einem weißen Häkchen angezeigt werden soll, um die Verbindung zu trennen.
Ich hoffe das hilft dir; Lass es mich wissen, wenn es so ist! :) :)
quelle