So beschleunigen Sie die Kompilierungszeit von g ++ (bei Verwendung vieler Vorlagen)


70

Diese Frage ist vielleicht irgendwie seltsam, aber wie kann ich die Kompilierungszeit von g ++ beschleunigen? Mein C ++ - Code verwendet stark Boost und Vorlagen. Ich habe bereits so viel wie möglich aus den Header-Dateien entfernt und die Option -j verwendet, aber das Kompilieren (und Verknüpfen) dauert immer noch eine Weile.

Gibt es Tools, die meinen Code analysieren und auf Engpässe für den Compiler hinweisen? Oder kann man den Compiler, der auf meinem Code läuft, irgendwie profilieren? Das wäre wirklich schön, denn manchmal habe ich den Eindruck, dass ich zu viel Zeit damit verbracht habe, auf das Compiler-Konsolenprotokoll zu starren ...



1
@Neil: Zu lang. Insbesondere wenn sich eine weit verbreitete Vorlagen-Header-Datei ändert, wird fast jeder Code neu kompiliert.
Danvil

Antworten:


54

Was war für mich am nützlichsten:

  • Bauen Sie auf einem RAM-Dateisystem auf. Dies ist unter Linux trivial. Möglicherweise möchten Sie auch eine Kopie der gängigen Header-Dateien (vorkompilierte oder die tatsächlichen .h-Dateien) im RAM-Dateisystem behalten.
  • Vorkompilierte Header . Ich habe eine pro (Haupt-) Bibliothek (zB Boost, Qt, stdlib).
  • Deklarieren Sie nach Möglichkeit Klassen, anstatt sie einzuschließen. Dies reduziert Abhängigkeiten und damit die Anzahl der Dateien, die beim Ändern einer Header-Datei neu kompiliert werden müssen.
  • Make parallelisieren . Dies hilft normalerweise von Fall zu Fall, aber ich habe -j3global zu machen. Stellen Sie jedoch sicher, dass Ihre Abhängigkeitsdiagramme in Ihrem Makefile korrekt sind. Andernfalls können Probleme auftreten.
  • Verwenden -O0Sie diese Option, wenn Sie die Ausführungsgeschwindigkeit oder die Codegröße nicht testen (und Ihr Computer schnell genug ist, damit Sie sich nicht um den (wahrscheinlich kleinen) Leistungseinbruch kümmern).
  • Kompilieren Sie jedes Mal, wenn Sie speichern. Einige Leute mögen das nicht, aber es ermöglicht Ihnen, Fehler frühzeitig zu erkennen und kann im Hintergrund ausgeführt werden, wodurch sich die Wartezeit verringert, wenn Sie mit dem Schreiben fertig sind und zum Testen bereit sind.

1
Zu Ihrer Information, ich habe gerade versucht, einen vorkompilierten Header mit einer Reihe von Boost- und STL-Bibliotheken zu erstellen. Ich habe es auf RAM fs gelegt. keine Beschleunigung.
kirill_igum

Ich denke, dies hat meine Kompilierungszeit (nicht vollständig) um etwa das Zehnfache verkürzt.
Evan Carslake

2
Ich bezweifle, dass der Einbau eines RAM-Dateisystems irgendetwas verbessern würde. Wenn Sie über genügend RAM verfügen, speichert der Linux-Kernel nur Schreib- und Lesevorgänge im Cache. Daher sollte das Lesen / Schreiben von / in Dateien bereits RAM verwenden.
Hi-Angel

17

Folgendes habe ich getan, um Builds in einem sehr ähnlichen Szenario zu beschleunigen, das Sie beschreiben (Boost, Vorlagen, GCC).

  • Aufbau auf lokaler Festplatte anstelle eines Netzwerkdateisystems wie NFS
  • Upgrade auf eine neuere Version von gcc
  • distcc untersuchen
  • Schnellere Build-Systeme, insbesondere mehr RAM

4
icecream / icecc ist besser als distcc: "Im Gegensatz zu distcc verwendet Icecream einen zentralen Server, der die Kompilierungsaufträge dynamisch auf dem schnellsten freien Server plant."
Zitrax

1
ccache hilft nur, wenn Sie dieselbe Quelle erneut kompilieren. Dies ist bei der Entwicklung normalerweise nicht der Fall, da Make dies bereits erkennen sollte und der Compiler daher nicht erneut gestartet wird. ccache hilft für dieselben Dateien in verschiedenen Verzeichnissen, z. B. alle "configure" -Tests auf Linux-Systemen oder wenn Sie dieselben (oder ähnliche) Quellen in einem anderen Verzeichnis bereinigt oder kompiliert haben. Dies geschieht häufig in Linux-Quelldistributionen wie Gentoo, wo nach einem Kompilierungsfehler und einem Neustart das Arbeitsverzeichnis der Kompilierung bereinigt wird. In diesem Szenario kann ccache die Kompilierung erheblich beschleunigen.
IanH

5
ccacheDies ist sehr hilfreich, wenn Sie DVCS verwenden oder in einem Team arbeiten, da in beiden Fällen die Datei wahrscheinlich bereits kompiliert wurde, obwohl sie von einem anderen Speicherort stammt.
Matthieu M.

17

Ich gehe davon aus, dass es sich um Minuten zum Kompilieren einer Datei handelt, dh vorkompilierte Header oder lokale Festplattenprobleme sind nicht das Problem.

Lange Kompilierungszeiten mit tiefem Vorlagencode (Boost usw.) basieren häufig auf dem unfreundlichen asymptotischen Verhalten von gcc bei der Instanziierung von Vorlagen, insbesondere wenn verschiedene Vorlagen mit Standardargumenten für Vorlagen emuliert werden.

Hier ist ein Dokument, in dem die reduzierte Kompilierungszeit als Motivation für verschiedene Vorlagen genannt wird:

cpptruths hatte einen Artikel darüber, wie viel besser gcc-4.5 in diesem Zusammenhang ist und wie es mit seinen verschiedenen Vorlagen hervorragend funktioniert:

IIRC dann hat BOOST eine Möglichkeit, die Generierung von Template-Standardparametern für die Pseudovariadics zu begrenzen. Ich denke, 'g ++ -DBOOST_MPL_LIMIT_LIST_SIZE = 10' sollte funktionieren (der Standard ist 20).

UPDATE: Es gibt auch einen schönen Thread mit allgemeinen Techniken, um das Kompilieren hier auf SO zu beschleunigen, was nützlich sein könnte:

UPDATE: Hier geht es um die Leistungsprobleme beim Kompilieren von Vorlagen. In der akzeptierten Antwort wird auch gcc-4.5 empfohlen. Als positives Beispiel wird auch clang genannt:


3
siehe auch gcc.gnu.org/gcc-4.5/changes.html :Compilation time for code that uses templates should now scale linearly with the number of instantiations rather than quadratically, as template instantiations are now looked up using hash tables.
Sebastian Mach

10

Wenn Sie viel neu kompilieren, kann Ccache hilfreich sein. Es beschleunigt die Kompilierung nicht wirklich, aber es gibt Ihnen ein zwischengespeichertes Ergebnis, wenn Sie aus irgendeinem Grund eine nutzlose Neukompilierung durchführen. Es mag den Eindruck erwecken, das falsche Problem zu lösen, aber manchmal sind die Wiederherstellungsregeln so kompliziert, dass Sie während eines neuen Builds tatsächlich denselben Kompilierungsschritt ausführen.

Zusätzliche Idee: Wenn Ihr Code mit clang kompiliert wird , verwenden Sie ihn stattdessen. Es ist normalerweise schneller als gcc.


3

Zusätzlich zu dem, was alle anderen hinzugefügt haben und was Sie bereits tun (parallelisierter Build, Compileroptionen usw.), sollten Sie Vorlagen in Implementierungsklassen ausblenden, auf die über Schnittstellen zugegriffen wird. Das bedeutet, dass anstelle einer Klasse wie:

// ClsWithNoTemplates.h file, included everywhere

class ClsWithTemplates
{
    ComplicatedTemplate<abc> member;
    // ...

public:
    void FunctionUsingYourMember();
};

du solltest haben:

// ClsWithNoTemplates.h file:

class ClsWithTemplatesImplementation; // forward declaration
  // definition included in the ClsWithNoTemplates.cpp file
  // this class will have a ComplicatedTemplate<abc> member, but it is only 
  // included in your ClsWithNoTemplates definition file (that is only included once)


class ClsWithNoTemplates
{
     ClsWithTemplatesImplementation * impl; // no templates mentioned anywhere here
public:
    void FunctionUsingYourMember(); // call impl->FunctionUsingYourMember() internally
};

Dies ändert Ihr OOP-Design ein wenig, ist aber gut so: Die Definition von 'ClsWithNoTemplates' ist jetzt schnell und Sie kompilieren die Definition von 'ClsWithNoTemplates' nur einmal (vor).

Wenn Sie den Implementierungscode ändern, muss der Code, der ClsWithNoTemplates.h enthält, wahrscheinlich nicht neu definiert werden.

Diese Änderung sollte Ihre teilweise Kompilierungszeit drastisch verlängern und hilft auch in dem Fall, in dem Ihre ClsWithNoTemplates eine öffentliche Schnittstelle ist, die aus einer Bibliotheksdatei exportiert wird: Da die Datei nicht geändert wird, wenn Sie nur die Implementierung ändern, wird Ihr abhängiger Clientcode nicht verwendet. Es muss überhaupt nicht neu kompiliert werden.



2

Wenn viele Dateien vorhanden sind, können Sie die Kompilierung erheblich beschleunigen, indem Sie nur eine CPP-Datei verwenden, die alle anderen CPP-Dateien enthält. Dies erfordert natürlich, dass Sie mit Makros vorsichtiger umgehen und diese bereits pro Datei definiert haben, da sie jetzt für andere CPP-Dateien sichtbar sind.

Wenn viele Dateien vorhanden sind, kann dies die Kompilierungszeit erheblich verkürzen.


Könnte sein. Bis Ihr Compiler den Speicher auffrisst und mit dem Auslagern beginnt.
KeithB

Kombinieren Sie es einfach in vernünftigen Abschnitten. Ich sage nicht, dass Sie 3 Millionen Codezeilen in eine einzige Datei einfügen sollten. Ich verwende diese Technik bei einem großen Projekt mit sehr guten Ergebnissen, bei einigen Compilern bis zu 1/5 der ursprünglichen Zeit.
Zitrax

1

Instanziieren Sie weniger Vorlagen und Inline-Funktionen. Kompilieren Sie so viel wie möglich vor und verknüpfen Sie es einfach, anstatt alles von Grund auf neu zu kompilieren. Stellen Sie sicher, dass Sie die neueste Version von GCC verwenden.

Es ist jedoch eine einfache Tatsache, dass C ++ eine unglaublich komplexe Sprache ist und das Kompilieren einige Zeit in Anspruch nimmt.


1

In diesem Dokument wird eine Methode zum Kompilieren von Vorlagencode beschrieben, ähnlich wie bei "herkömmlichen" Nicht-Vorlagen-Objektdateien. Spart Kompilierungs- und Verknüpfungszeit mit nur einer Codezeile pro Vorlageninstanziierung.


Ich kann das PDF für diesen Artikel scheinbar nicht herunterladen, ohne dass es sich anmelden möchte. Müssen Sie tatsächlich bezahlen, um Zugriff auf dieses Dokument zu erhalten?
Urwolf

0

Normalerweise sind die teuersten Teile der Kompilierung (a) das Lesen der Quelldateien ( ALL ) und (b) Laden des Compilers in den Speicher für jede Quelldatei.

Wenn Sie 52 Quelldateien (.cc) haben, von denen jede # 47 # Einschlussdateien (.h) enthält, laden Sie den Compiler 52 Mal und durchsuchen 2496 Dateien. Abhängig von der Dichte der Kommentare in den Dateien verbringen Sie möglicherweise viel Zeit damit, nutzlose Zeichen zu essen. (In einer Organisation, die ich gesehen habe, variierten die Header-Dateien zwischen 66% und 90% der Kommentare, wobei nur 10% bis 33% der Datei "aussagekräftig" waren. Das Beste, was getan werden konnte, um die Lesbarkeit dieser Dateien zu verbessern, war Strip jeden letzten Kommentar auslassen und nur Code hinterlassen.)

Sehen Sie sich genau an, wie Ihr Programm physisch organisiert ist. Prüfen Sie, ob Sie Quelldateien kombinieren und Ihre Hierarchie der # include-Dateien vereinfachen können.

Vor Jahrzehnten haben Unternehmen wie IBM dies verstanden und ihre Compiler so geschrieben, dass dem Compiler eine Liste der zu kompilierenden Dateien übergeben werden konnte, nicht nur eine Datei, und der Compiler nur einmal geladen wurde.


2
Wie lange dauert das "Kompilieren" von Kommentaren? (Es wird vom Präprozessor gemacht und sollte nicht sehr kompliziert sein, verglichen mit dem Sinn der Vorlagen-Metaprogrammierung?)
UncleBens

Also kann ich g ++ das nicht? Und gibt es ein Tool, das mir sagt, wie viele Include-Dateien der Compiler für eine bestimmte Quelldatei lesen muss?
Danvil

7
Es ist unwahrscheinlich, dass der Compiler mehrmals geladen wird. Es bleibt mit ziemlicher Sicherheit nach der ersten Datei im Festplatten-Cache, zusammen mit allgemeinen Header-Dateien. Computerarchitekturen und -geschwindigkeiten haben sich im Laufe der Jahrzehnte stark verändert, und die allgemeine Weisheit aus der Blütezeit von IBM ist heute möglicherweise nicht mehr so ​​nützlich.
Mike Seymour

4
-1, Codekommentare sind sinnvoll und das Entfernen kann gefährlich sein. Durch das Entfernen von Kommentaren wird der Quellcode nicht "lesbarer", wie Sie behaupten
Malfist

@Danvil, der Schlüssel ist, dass der Compiler den Kommentar LESEN muss, um zu den ausführbaren Zeilen nach dem Kommentar zu gelangen. Selbst wenn es vom Präprozessor "gelesen" wird, muss es noch gelesen werden, und Sie zahlen immer noch die Kosten für das Lesen.
John R. Strohm
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.