In diesem Beitrag behandeln wir Tipps und Tricks, wie Sie Ihre C++Builder-Projekte optimieren können, um so schnell wie möglich zu kompilieren. Während wir definitiv einige Zeit damit verbringen werden, Optimierungen für die Verwendung mit TwineCompile zu betrachten, gelten einige dieser Tricks für C++Builder im Allgemeinen.
C++Builder und TwineCompile enthalten viele Funktionen zur Optimierung der Kompilierungsgeschwindigkeit, die jedoch häufig angepasst werden müssen, da jedes Projekt einzigartig ist und unterschiedliche Bedingungen und Einstellungen hat. Wir werfen einen Blick auf verschiedene Tipps und wann und wie Sie diese auf verschiedene Projekttypen und -strukturen anwenden können.
Table of Contents
Trick #1 – TwineCompile
Der erste Trick ist einfach – beginnen Sie mit TwineCompile! TwineCompile ist ein IDE-Plugin, das sich in C++Builder integriert und den C++Builder-Compilern (sowohl Classic als auch CLANG) Multithreading, Caching und andere leistungsstarke Verbesserungen hinzufügt. In einigen Fällen führt die einfache Installation von TwineCompile dazu, dass Projekte auf Build-Workstations der neuesten Generation 50-mal schneller kompiliert werden.
TwineCompile steht allen Benutzern von C++Builder 10.4.1+ mit einer aktiven Update Subscription über den GetIt Package Manager kostenlos zur Verfügung. Dies sollte die erste Station auf dem Weg nach schnelleren Kompilierungszeiten sein, da Sie diese Leistungsverbesserungen nutzen können, ohne eine einzige Änderung an Ihrer Projektstruktur oder Ihren Dateien vornehmen zu müssen (im Gegensatz zu den folgenden Tipps).
Um TwineCompile zu erhalten, öffnen Sie den GetIt Package Manager in der IDE und suchen Sie nach TwineCompile. Folgen Sie den Anweisungen, um es zu installieren.
Die IDE-Integration von TwineCompile wird automatisch die IDE Compile/Make/Build-Befehle einhaken, um TwineCompile anstelle des Build-Prozesses der IDE aufzurufen. Auf diese Weise können Sie die IDE weiterhin wie heute verwenden und die von TwineCompile gebotene Kompilierleistung voll ausnutzen.
TwineCompile Trick #2 – CLANG Powered PCH (vorkompilierte Header)
C++Builder verfügt seit langem über eine Geheimwaffe, die bei langsamen Kompilierungszeiten zaubern kann. Die effektivste Verbesserung besteht oft darin, vorkompilierte Header in einem Projekt richtig einzurichten und zu verwenden. Die Einführung des CLANG-Compilers hat diese Funktionalität deutlich verbessert und die Bedienung erheblich vereinfacht. Wenn Ihr Projekt derzeit den CLANG-Compiler verwendet, würde ich Ihnen dringend empfehlen, die PCH-Unterstützung in CLANG zu nutzen, da dies Ihre Kompilierungszeiten erheblich verbessern wird. Wenn Ihr Projekt noch den Classic-Compiler verwendet, würde ich empfehlen, es auf den CLANG-Compiler zu aktualisieren – Sie erhalten nicht nur die Möglichkeit, einen modernen, standardkonformen C++-Compiler mit C++v17-Unterstützung zu verwenden, sondern Sie werden auch auch das CLANG PCH-System nutzen können.
Das Konzept hinter vorkompilierten Headern ist ziemlich einfach – nehmen Sie eine Reihe von Headern, die in den meisten (wenn nicht allen) Einheiten eines Projekts verwendet werden, und kompilieren Sie sie in eine binäre Darstellung. Wenn eine Unit des Projekts diese vorkompilierten Header enthält, muss der Compiler die Header nicht von Grund auf neu verarbeiten/kompilieren, sondern kann einfach die vorkompilierte Darstellung von der Festplatte laden und mit dem Kompilieren des Codes in der Unit fortfahren .
Es ist eine gewisse Disziplin erforderlich, da die Header-Reihenfolge in jeder Unit identisch sein muss, um sicherzustellen, dass der Compiler den vorkompilierten Header verwenden kann. Außerdem sollte der vorkompilierte Header keine Header enthalten, die häufig geändert werden, da der Aufwand für das Kompilieren der Header ziemlich hoch ist und Sie alle Vorteile des vorkompilierten Headers verlieren, wenn er ständig neu erstellt werden muss.
- Beginnen Sie damit, Ihr Projekt zu analysieren und die Kopfzeilen zu identifizieren, die die meisten Einheiten enthalten. Dies kann eine Reorganisation erfordern, damit die meisten oder alle Einheiten denselben Header-Satz enthalten. Beachten Sie, dass einschließlich zusätzlicher, unbenutzter,
headers hat keinen negativen Effekt. Wenn also einige Einheiten nicht alle Header verwenden, ist es völlig in Ordnung. Je mehr Header vorkompiliert sind, desto effektiver können Sie Ihre Kompilierungszeiten verbessern.
- Wenn Ihr Projekt bereits über eine PCH-Headerdatei verfügt (z. B. die PCH-.h-Datei, die automatisch von der IDE für neue Projekte generiert wird), fahren Sie mit Schritt 6 fort.
- Wenn Ihr Projekt keine PCH-Header-Datei hat (wie die PCH .h-Datei, die automatisch von der IDE für neue Projekte generiert wird), fügen Sie dem Projekt eine neue Header-Datei hinzu:
- Speichern Sie die neue Headerdatei unter einem beschreibenden Namen wie ProjectNamePCH.h
- Klicken Sie im Projektbereich mit der rechten Maustaste auf die Headerdatei und wählen Sie die Option Für die Vorkompilierung verwenden:
- Öffnen Sie diese Vorkompilierungs-Header-Datei und fügen Sie alle Include-Anweisungen ein, die Sie in Schritt 1 gesammelt haben. Beispielsweise:
#include <vcl.h>
#include <tchar.h>
- Gehen Sie alle Units in Ihrem Projekt durch und entfernen Sie die #include-Anweisungen, die sich auf die Header im PCH-Header beziehen. Fügen Sie am Anfang jeder Unit vor allen anderen Anweisungen die folgende Pragma-Anweisung ein, um dem Compiler mitzuteilen, dass die Unit den vorkompilierten Header nicht anpasst:
#pragma hdrstop
- Öffnen Sie die Projektoptionen, gehen Sie zum Abschnitt Vorkompilierte Header unter C++-Compiler und wählen Sie Generieren und verwenden für die PCH-Verwendungsoption
- Erstellen Sie das Projekt. Es dauert einige Zeit, die PCH-Header-Datei in die Binärdarstellung zu kompilieren, und dann werden die nachfolgenden Units um ein Vielfaches schneller kompiliert, da diese Header nicht mehr für jede Unit immer wieder verarbeitet werden müssen.
Trick #3 – TwineCompile Single-Header PCH für den Classic Compiler
Wenn Ihr Projekt noch den Classic-Compiler verwendet und Sie es nicht auf den neueren CLANG-Compiler migrieren können, ist es immer noch möglich, durch die Verwendung vorkompilierter Header erhebliche Kompiliergeschwindigkeitsgewinne zu erzielen, da der klassische Compiler ein erstaunlich leistungsstarkes PCH-System hat. TwineCompile wurde entwickelt, um die Vorteile dieses Systems voll auszuschöpfen und ordnet den Erstellungsprozess so an, dass der klassische Compiler automatisch mit den richtigen Parametern für jede Datei aufgerufen wird, die den vorkompilierten Header verwendet.
Im Gegensatz zum CLANG-Compiler ist der klassische Compiler darauf ausgelegt, den vorkompilierten Header als Teil der Kompilierung der regulären Quelldateien zu generieren und zu verwenden. Es ist kein separater Kompilierungsschritt erforderlich, um eine Datei zu kompilieren, die als vorkompilierte Headerdatei bezeichnet wurde.
Es gibt zwei Möglichkeiten, ein Projekt so einzurichten, dass ein PCH mit einem einzelnen Header mit dem klassischen Compiler verwendet wird:
- Verwenden eines Injektionsbefehls zum automatischen Einfügen der Einzelheader-PCH-Datei in jede Unit als Teil des Kompilierungsprozesses.
- Manuelles Hinzufügen der #include-Anweisung für die Einzelheader-PCH-Datei am Anfang jeder Unit.
Der erste Ansatz ist offensichtlich der einfachste, da er dafür sorgt, dass der Header automatisch in jeder Unit verfügbar ist. Der zweite Ansatz kann nützlich sein, da der Header tatsächlich in jeder Unit enthalten ist, sodass er zum Durchsuchen und für andere Zwecke wie Codeanalysetools verfügbar ist.
- Beginnen Sie wie bei den CLANG-Schritten damit, Ihr Projekt zu analysieren und die Header zu identifizieren, die die meisten Einheiten enthalten.
Dies kann eine gewisse Reorganisation erfordern, damit die meisten oder alle Einheiten denselben Header-Satz enthalten. Beachten Sie, dass das Einschließen zusätzlicher, nicht verwendeter Header keine negativen Auswirkungen hat. Wenn also einige Einheiten nicht alle Header verwenden, ist dies völlig in Ordnung. Je mehr Header vorkompiliert sind, desto effektiver können Sie Ihre Kompilierungszeiten verbessern.
- Erstellen Sie eine neue Header-Datei in Ihrem Projekt:
- Speichern Sie die neue Headerdatei unter einem beschreibenden Namen wie ProjectNamePCH.h
- Öffnen Sie diese Vorkompilierungs-Header-Datei und fügen Sie alle Include-Anweisungen ein, die Sie in Schritt 1 gesammelt haben. Beispielsweise:
#include <vcl.h>
#include <tchar.h>
5a. Ansatz A – Wenn Sie die Injection-Funktionalität verwenden möchten, öffnen Sie die Projektoptionen, gehen Sie zum Abschnitt Vorkompilierte Header unter C++-Compiler und geben Sie den Header-Dateinamen in die Option Inject-Header ein:
5b. Ansatz B – Wenn Sie den Header manuell zu Ihren Units hinzufügen möchten, platzieren Sie die #include-Anweisung für diese Header-Datei gefolgt von einer #pragma hdrstop-Zeile ganz oben in allen Units. Beispielsweise:
#include "ProjectNamePCH.h"
#pragma hdrstop
- Öffnen Sie die Projektoptionen, gehen Sie zum Abschnitt Vorkompilierte Header unter C++-Compiler:
6a. Wählen Sie die Option Generieren und verwenden für die PCH-Nutzung. Dies weist TwineCompile an, die erste Quellcodedatei zu verwenden, um die PCH-Datei zu generieren und sie für alle nachfolgenden Einheiten zu verwenden.
6b. Wählen Sie die Option Vorkompilierte Header zwischenspeichern
6c. Geben Sie einen Namen für die generierte PCH-Datei in die Option PCH-Dateiname ein. Beispiel: Project.csm. Diese Datei enthält die Binärdaten, die aus dem als vorkompilierten Header ausgewählten Header kompiliert wurden.
Die Optionen sollten ungefähr so aussehen:
- Erstellen Sie das Projekt. TwineCompile passt die Compileroptionen für die zu kompilierenden Dateien automatisch an, sodass die PCH-Datei generiert und als Teil des Kompilierungsprozesses verwendet wird, was die Kompilierungszeiten erheblich verbessert.
Trick #4 – TwineCompile automatischer PCH für den Classic-Compiler
Für Projekte, die nicht auf den CLANG-Compiler migriert werden können und nicht über die Projektstruktur verfügen, um eine einzelne PCH-Datei zu unterstützen, die für alle Units funktioniert, ist die Hoffnung nicht verloren. Der Classic-Compiler bietet in Verbindung mit TwineCompile einige erweiterte PCH-Funktionen, die die meisten Leistungsvorteile eines optimierten vorkompilierten Headers für diese Art von Projekten bieten. Der Classic-Compiler unterstützt das Konzept eines „adaptiven“ (meine Worte, nicht die offizielle Beschreibung) vorkompilierten Headers, der kontinuierlich erstellt und als Teil eines Projekt-Builds angepasst wird, um die
verschiedene Variationen von Headern, die die Einheiten des Projekts verwenden können. Der Trick besteht darin, es in Ihrem Projekt zum Laufen zu bringen, und hier kommt TwineCompile ins Spiel.
- Öffnen Sie die Projektoptionen, gehen Sie zum Abschnitt Vorkompilierte Header unter C++-Compiler:
1a. Wählen Sie die Option Generieren und verwenden für die PCH-Nutzung. Beachten Sie, dass dieses Projekt keine PCH-Struktur hat, daher würde diese Option normalerweise mehr Probleme verursachen als sie löst.
1b. Wählen Sie die Option Vorkompilierte Header zwischenspeichern
1c. Geben Sie einen Namen für die generierte PCH-Datei in die Option PCH-Dateiname ein. Beispiel: Project.csm. Diese Datei enthält die Binärdaten, die aus dem als vorkompilierten Header ausgewählten Header kompiliert wurden.
Die Optionen sollten ungefähr so aussehen:
- Öffnen Sie die TwineCompile-Optionen aus dem TwineCompile-Menü, gehen Sie zu Vorkompilierte Header und wählen Sie die Option „PCH-Datei für jeden Thread verwenden“. OK klicken.
- Erstellen Sie das Projekt. TwineCompile passt die Compiler-Optionen für jede Datei automatisch an, sodass sie eine PCH-Datei generieren und verwenden können, die sich an die verschiedenen Variationen der in jeder Unit enthaltenen Header anpasst. Die Leistung wird nicht ganz so gut sein wie bei einem richtigen PCH-Setup, aber deutlich besser, als wenn PCH überhaupt nicht verwendet wird.
TwineCompile Trick #5 – Optimierung Ihres Projektlayouts
Oder anders ausgedrückt – die Anzahl der Dateien, die bei einer Änderung kompiliert werden müssen, zu minimieren, dies jedoch gegen die Zeit zum Linken abzuwägen.
Eine übliche Taktik zur Optimierung der C++-Kompilierungszeiten besteht darin, Projekte in viele kleine Bibliotheken oder Pakete aufzuteilen. Die Idee ist, dass, wenn Code geändert wird, nur die wenigen Dateien in diesem Projekt neu kompiliert werden müssen. Die neu kompilierte Bibliothek kann einfach mit den anderen Bibliotheken neu verknüpft werden, um das Endergebnis zu bilden.
Es gibt zwei Gründe, warum dies oft eine schlechte Idee ist, insbesondere wenn Sie TwineCompile verwenden:
- Sie handeln Compile-Zeit mit Link-Zeit. Ratschläge, die diesen Ansatz empfehlen, beziehen sich im Allgemeinen auf langsamere C++-Compiler wie gcc, bei denen die Linkzeit viel schneller ist als die sehr langsamen Kompilierungszeiten. Daher kann es hilfreich sein, die Arbeit auf den Linker auszulagern. Die C++Builder-Compiler (sowohl CLANG als auch Classic) sind für C++-Compiler ziemlich schnell, sodass das Hinzufügen von 15 Linkschritten, um das Kompilieren von 30 C++-Dateien zu vermeiden, oft zu einem erheblichen Leistungsverlust führt.
- Das Verknüpfen ist ein serieller Prozess. Die Kompilierung erfolgt parallel. Wenn Sie TwineCompile verwenden, werden C++-Dateien parallel kompiliert. Je mehr Einheiten gleichzeitig kompiliert werden können, desto mehr Leistung lässt sich also erzielen. Ein einzelnes Projekt mit 200 C++-Einheiten und einem einzelnen Linkschritt wird um ein Vielfaches schneller erstellt als 10 Projekte mit 20 C++-Einheiten, jedes mit eigenem Linkschritt.
Wie bei den meisten Dingen im Leben gibt es einen Kompromiss, bei dem eine geteilte Projektstruktur sinnvoll ist. Leider gibt es keine einzige richtige Antwort darauf, wie ein Projekt aufgebaut sein sollte, daher überlasse ich Ihnen die folgenden Richtlinien:
- Minimieren Sie die Anzahl der Dateien, die für jede Änderung der Quelle kompiliert werden müssen.
- Minimieren Sie die Anzahl der zu verknüpfenden Bibliotheken, Pakete oder Anwendungen. Selbst statische Bibliotheken sind zwar relativ schnell zu verknüpfen, haben aber einen Overhead.
- Maximieren Sie Ihre Prozessorressourcen. Der Linker verwendet nur einen Kern, 16 C++-Dateien auf einem 8-Kern + HT-Prozessor verwenden alle 16 logischen Kerne. Das Kompilieren dieser 16 Dateien kann also 16x mehr Ressourcen verbrauchen als der Linker.
Trick #6 – SORTA Kompilieren
Eine der mächtigsten Funktionen von TwineCompile ist das automatische Hintergrundkompilierungssystem. Im Wesentlichen kompiliert diese Funktion Ihre geänderten Quellcodedateien automatisch im Hintergrund, sodass beim Klicken auf Erstellen oder Ausführen keine Dateien zu kompilieren sind, alles auf dem neuesten Stand ist und der Buildprozess nur die Anwendung verknüpfen muss.
Im Gegensatz zu vielen der oben genannten Tricks ist dieser nicht direkt daran gebunden, den Kompilierungs- oder Build-Prozess zu beschleunigen, sondern zielt darauf ab, die Notwendigkeit des Kompilierens des Quellcodes zu reduzieren oder sogar zu eliminieren.
Um SORTA Compile zu aktivieren, öffnen Sie die TwineCompile Options, gehen Sie zum Abschnitt SORTA Compile und wählen Sie die Enable SORTA Compile Option:
Es gibt eine Reihe von Auslösern, die verwendet werden, um diese Funktionalität aufzurufen, einschließlich wenn eine Einheit gespeichert wird, wenn sich die Registerkarte des Editors ändert oder wenn eine Datei geändert und für einen bestimmten Zeitraum nicht berührt wird.
Die effektive Verwendung von SORTA Compile erfordert zwar eine Anpassung des Codierungsworkflows, kann aber dem traditionellen vierstufigen Projektentwicklungsprozess enorme Vorteile bieten: Bearbeiten, Kompilieren, Ausführen, Testen, Wiederholen.
TwineCompile Trick #7 – Statische versus dynamische Verknüpfung
Wenn Sie die oben genannten Tricks auf Ihre Projekte anwenden, werden Sie wahrscheinlich feststellen, dass Sie die ganze Zeit auf den Linker warten. Das Warten auf den Compiler wird fast vollständig verschwinden. Dies ist sowohl eine gute als auch eine schlechte Sache. Gut, weil es
bedeutet, dass Sie die ganze Zeit sparen, schlecht, weil Sie nicht viel mehr tun können, um die Build-Leistung zu optimieren.
Vor diesem Hintergrund können einige Anwendungen umstrukturiert werden, um verschiedene Funktionalitäten in separate DLLs zu packen, die von der Anwendung zur Laufzeit geladen werden. Dies bedeutet, dass bei Codeänderungen an einem bestimmten Teil der Anwendung nur dieser spezifische Code kompiliert und in eine eigene DLL-Datei eingebunden wird. Der Rest der Anwendung muss nicht neu erstellt werden, da sie zur Laufzeit mit dieser DLL verknüpft wird.
Offensichtlich fügt dieser Ansatz den Bereitstellungs- und Verwaltungsaufwand für zusätzliche DLL-Dateien hinzu, die mit der Anwendung gebündelt werden müssen, aber die Einsparungen bei der Buildleistung können den zusätzlichen Aufwand oft wert sein.
Trick #6 – Hardware
Unser letzter Trick ist ein alter und bekannter Trick, oft bekannt als „Hardware auf das Problem werfen“. Während neuere, schnellere Hardware in der Vergangenheit nur geringe Vorteile bot, können die neuesten Hardware-Fortschritte von heute in Verbindung mit der Fähigkeit von TwineCompile, die Nutzung der Systemressourcen zu maximieren, einige bedeutende Vorteile bieten.
Es gibt zwei Schwerpunkte, auf die man sich konzentrieren sollte:
1. Festplattenleistung
Dies bedeutet SSD-Laufwerke, die NVMe/PCIe-Verbindungen verwenden. SATA-SSDs sind gut, aber es fehlt die Direktzugriffsleistung von SSDs, die über den PCIe-Bus betrieben werden.
Spindeln sind ein absolutes No-Go. Wenn sich Ihre Projekte, temporären Verzeichnisse, Ihr Betriebssystem oder der C++Builder-Installationspfad auf einer Spindel befinden, ist Ihre Build-Leistung miserabel.
2. CPU-Kerne
Eine geradlinige Single-Core-Performance für die C++-Kompilierung ist heutzutage weniger nützlich als viele Kerne. Entscheiden Sie sich für die maximal verfügbare Anzahl von Kernen.
AMD Threadripper Prozessoren mit 64 Kernen (128 Threads) sind ideal für die C++-Kompilierung in C++Builder mit TwineCompile. Beachten Sie, dass Sie bei einem Prozessor wie diesem, wenn Sie weniger als 128 C++-Dateien in Ihrem Projekt haben, CPU-Ressourcen verschwenden – dies geht auf den Punkt zurück, den wir in Trick #5 zur Optimierung Ihres Projektlayouts zur Maximierung der CPU-Auslastung gemacht haben Ressourcen, indem Sie viele Dateien im selben Projekt kompilieren.
Abschließende Gedanken
Wir haben einen langen Weg zurückgelegt seit den alten Tagen der C++-Kompilierung, als Builds lange dauerten und nichts dagegen unternommen werden konnte. Es wurden Werkzeuge und Techniken entwickelt, die bei Projekten jeder Größe einen entscheidenden Unterschied machen. Wir haben in diesem Beitrag eine Reihe davon behandelt und Sie können sie hoffentlich auf Ihre eigenen Projekte anwenden, um Ihre C++-Entwicklungserfahrung schneller, einfacher und einfacher zu machen.
RAD Studio C++ Builder, warum probieren Sie es nicht gleich heute aus?
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition