Was ist der beste Ansatz für das Codieren in einer langsamen Kompilierungsumgebung?


15

Ich war es gewohnt, in C # im TDD-Stil zu codieren - einen kleinen Codeabschnitt zu schreiben / oder zu ändern, die gesamte Lösung in 10 Sekunden neu zu kompilieren, die Tests erneut auszuführen und noch einmal. Einfach...

Diese Entwicklungsmethode hat für mich einige Jahre lang sehr gut funktioniert, bis ich letztes Jahr wieder auf C ++ zurückgreifen musste und das Gefühl hat, dass meine Produktivität seitdem dramatisch abgenommen hat. Das C ++ als Sprache ist kein Problem - ich hatte ziemlich viel Erfahrung mit C ++ - Entwicklern ... aber in der Vergangenheit.

Meine Produktivität ist für kleine Projekte noch in Ordnung, aber es wird schlimmer, wenn die Projektgröße zunimmt und wenn die Kompilierungszeit mehr als 10 Minuten beträgt, wird es wirklich schlimm. Und wenn ich den Fehler finde, muss ich erneut mit dem Kompilieren beginnen usw. Das ist einfach nur frustrierend.

Daher kam ich zu dem Schluss, dass ein kleiner Teil (wie zuvor) nicht akzeptabel ist - Empfehlungen, wie ich mich in die alte Gewohnheit des Codierens für etwa eine Stunde einarbeiten kann, wenn ich den Code manuell überprüfe (ohne mich auf einen schnellen C # -Compiler zu verlassen). und Komponententests nur einmal in ein paar Stunden neu kompilieren / erneut ausführen.

Mit C # und TDD war es sehr einfach, einen Code evolutionär zu schreiben - nach einem Dutzend Iterationen endete der Mist, mit dem ich angefangen hatte, in einem guten Code, aber er funktioniert für mich nicht mehr (in einer langsamen Kompilierung) Umgebung).


stackoverflow.com/questions/5078409/… Diese sollten wahrscheinlich an einer Stelle zusammengeführt werden.
Ben L

Siehe Laso stackoverflow.com/questions/373142/... für die Beschleunigung C ++ Kompilierungszeiten.
Eclipse

2
Starte einen Schwertkampf . ; p
Steven Jeuris

Das letzte Mal, als ich C ++ ernsthaft verwendete und vorkompilierte Header verwendete, reduzierte sich die Kompilierungszeit um den Faktor vier.
gnasher729

Antworten:


16

Mir fallen mehrere Dinge ein:

  1. Nutzen Sie die verteilte Zusammenstellung . Sie können dies mit GCC ("distCC"?) Oder VC tun ( Xoreax 'IncrediBuild ist nicht gerade billig, aber jeden Cent wert, der dafür ausgegeben wird.).

  2. Teilen Sie Ihr Projekt in dynamisch geladene Bibliotheken auf und versuchen Sie sorgfältig, die Abhängigkeiten davon zu minimieren. Kleinere ausführbare Dateien verbinden sich viel schneller.

  3. Programmieren Sie eher gegen kleine Testprojekte als gegen die gesamte große Anwendung.

  4. Verwenden Sie Template-Meta-Programmierung, um Algorithmen zur Kompilierungszeit auszuführen . Ja, dies verlängert zwar die Kompilierungszeiten, verringert jedoch auch die zum Testen erforderlichen Bearbeitungszeiten: Wenn die Kompilierung einwandfrei ist, ist sie abgeschlossen.

  5. In Hardware investieren . Mehr CPU-Kernel (auf Ihrem Computer oder in anderen) werden sich bei der verteilten Kompilierung wundern, und viel Speicher sowie eine schnelle Festplatte (SSD anstelle von HDD) werden viel dazu beitragen. Wenn Sie über ein 64-Bit-System und übermäßig viel RAM verfügen, kann das Kompilieren auf einer RAM-Festplatte zu einer unglaublichen Geschwindigkeitssteigerung führen.


1
Nach meiner Erfahrung sind Compiler-Caches eine bessere Idee als verteilte Kompilierung.
pqnet

Apropos RAM-Disk vs. SSD, ich war ziemlich überrascht, dass es nicht so viel Geschwindigkeit beim Kompilieren gebracht hat. Mein aktuelles Projekt (Java auf Android) wird in ca. 45 Sekunden von der SSD und in ca. 40 Sekunden von der RAM-Disk kompiliert (und die gesamte Toolchain befindet sich auf der RAM-Disk, nicht nur auf den Quellen). Keine dramatische Zunahme, würde ich sagen.
Haspemulator

10

Eine andere technische Lösung, die von anderen noch nicht erwähnt wurde, ist der Umstieg auf Solid State Drives anstelle von regulären Festplatten. In einem früheren Projekt, an dem ich gearbeitet habe, haben SSDs die Build-Zeiten von 30 auf 3 Minuten gesenkt.

Natürlich sind sie teuer. Berechnen Sie für Ihren Chef den Preis für verlorene Entwicklerzeit gegen den Preis für die einmalige Investition. Die Investition macht sich wahrscheinlich in wenigen Monaten bezahlt.


6
Das ist interessant. Das bedeutet, dass 90% der Erstellungszeit auf die E / A-Latenzzeit zurückzuführen sind.
Mike Dunlavey

Ich habe keinen Rückgang von 90% gesehen, aber einen deutlichen. Es scheint das Kompilieren nicht zu beschleunigen, aber es beschleunigt definitiv das Verknüpfen. Wenn Sie kleine Änderungen an einem großen Projekt vornehmen (es ist also nicht viel Kompilierung in einer Änderung) und erneut verknüpfen, erhalten Sie dies möglicherweise. (Dies ist Visual Studio 2008 unter Verwendung von C ++.)
David Thornley

1
Das ist eine gute Idee. Das Mounten eines Teils des Arbeitsspeichers in Ihrem Dateisystem funktioniert außerdem schnell und ist kostengünstig.
Goran Jovic

2
Ramdisk ist noch schneller (und billiger).
SK-logic

1
@ John: Ja, ein gut geschriebener Compiler sollte (IMHO) I / O-gebunden sein.
Mike Dunlavey

3

Mehr Planung, Code in größeren Blöcken, Schreiben von Integrationstests anstelle von Komponententests und Ausführen der build + -Testsuite über Nacht.


3

Lange Übersetzungszeiten sind manchmal ein Problem, aber die bereits erwähnte Modularisierung kann (meistens) helfen, das zu überwinden.

Weitaus schwerwiegender ist es, in einer Umgebung zu stecken, in der Sie überhaupt nicht kompilieren können und in der jede Codeänderung einer anderen Abteilung auf einem anderen Kontinent zur Anwendung in der Test- / Entwicklungsumgebung übermittelt werden muss. Dieser Prozess kann Tage in Anspruch nehmen.

Ich arbeite jetzt in einer solchen Umgebung und dieses System hat mich bereits über eine Woche Zeit gekostet (und das Projekt hat nur ein Budget von 4 Wochen, bevor das Geld ausgeht), nur um die erste Version unserer Änderungen zu installieren (Und dann haben sie Fehler gemacht, die dazu geführt haben, dass ein Teil der Dateien nicht vom Anwendungsserver abgerufen wurde. Wir haben also noch einige Tage mit Verzögerungen zu kämpfen.) Jede geringfügige Änderung (sagen wir, wir finden etwas in Tests, das behoben werden muss, z. B. eine verpasste Fehlerbedingung) kann zu einer Verzögerung von einem weiteren Tag oder mehr führen.

Unter solchen Umständen versuchen Sie so sicher wie möglich zu stellen, dass überhaupt keine Fehler vorliegen, bevor Sie versuchen, Ihren Code zu kompilieren. Es fühlt sich fast so an, als wäre ich zurück bei der Mainframe-Programmierung, wo wir für alle Kompilierungs- und Testarbeiten 5 Minuten CPU-Zeit pro Monat zur Verfügung hatten.


1
Junge, das ist eine Situation aus der Hölle. Ich erinnere mich an die Mainframe-Tage. Wir haben viel Code "am Schreibtisch überprüft". Es ist erstaunlich, wie viel auf diese Weise erreicht wurde, wie zum Beispiel endlose Simulationen von Flügen zum Mond.
Mike Dunlavey

3

Ich kann mich leicht erinnern, wann Builds lange gedauert haben. Einige mildernde Ansätze:

  • Erstellen Sie das System, indem Sie Bibliotheken oder DLLs kombinieren. Auf diese Weise muss beim Ändern von Code nur Ihr Teil neu kompiliert werden.
  • Die Anzahl der Punkte im Code, die Sie bearbeiten müssen, um eine Funktion zu implementieren, wirkt sich nicht nur darauf aus, wie viel Bearbeitung Sie ausführen müssen, sondern auch auf die Häufigkeit, mit der Sie Fehler einfügen, wodurch die Compile-Debug-Edit-Compile-Schleife verstärkt wird. Alles, was die Redundanz des Codes verringert, wie z. B. DRY, hilft.
  • Wenn Sie sich im Debugger befinden und bearbeiten, neu kompilieren und fortfahren können, ohne den Debugger zu verlassen, ist dies sehr hilfreich.

2

10+ Minuten für eine Kompilierung? Ernsthaft?

Verwenden Sie eine IDE, die inkrementelle Builds ausführt (z. B. Eclipse)? Wenn nicht, sollte dies wahrscheinlich der Fall sein, wird die Grundkompilierung in Sekunden und nicht in Minuten durchgeführt.

Oder handelt es sich um Integrationssachen, bei denen Sie die gesamte App erstellen müssen, um Ihre Änderung zu testen? In diesem Fall sollten Sie sich kleinere Tests ansehen, um sicherzustellen, dass die wichtigsten Fehler nicht in Ihrem Code enthalten sind, bevor Sie den vollständigen Build ausführen müssen.


5
10 Minuten ist klein. Ich habe für ein Projekt gearbeitet, dessen Kompilierung und Verknüpfung von Grund auf auf einem Single-Core-Computer, PCHs und allem dauerte. Natürlich würde es außer bei automatischen Release-Builds niemand auf nur einem Prozessorkern aufbauen, aber trotzdem ... Wenn Sie etwas in einem Header ändern müssten, der fast überall enthalten ist (String-Manipulation, Fehlerbehandlung), könnten Sie verrückt werden.
sbi

2
Früher (vor Jahren) auf einem System gearbeitet, dessen Kompilierung für einen vollständigen Build 48 Stunden gedauert hat. Natürlich wurde erst am Freitagabend mit einem kompletten Build begonnen, der hoffentlich fertig sein wird, wenn wir am Montag wieder im Büro sind. Wir haben stattdessen nach Bedarf kleine Module erstellt (z. B. eine einzelne DLL).
22.

2
-1 zu allen obigen Kommentaren, wenn ich +1 zu TrueDub geben könnte. Ja, wenn Sie alles neu kompilieren, kann dies sehr lange dauern. Wenn jedoch überhaupt über das Abhängigkeitsmanagement nachgedacht wird, können 10-stündige Neukompilierungsprojekte in weniger als einer Minute inkrementell neu kompiliert werden. Sie alle sollten sich dafür schämen, dass Sie Ihren Arbeitgebern Zeit verschwenden, indem Sie auf Ihre Neukompilierungen warten, wenn Sie ein bisschen Intelligenz anwenden. Das spart enorm viel Zeit.
Dunk

2
Und wenn Sie in einem kleinen Geschäft arbeiten, das sich zufällig in einem Projekt mit mehreren MLoCs befindet, bedeutet dies, dass ein beträchtlicher Teil des Codes alt ist, der vor einem Jahrzehnt als mehrere kleine Projekte begann, bei denen die Geschwindigkeit der Kompilierung nie ein Problem darstellte. und das Abhängigkeitsmanagement ist abscheulich schlecht. Wirst du einer solchen Firma sagen, sie solle das alles wegwerfen und ein weiteres Jahrzehnt damit verbringen, es neu zu schreiben?
sbi

2
@Dunk: Es war ein kleines Unternehmen mit weniger als einem Dutzend Entwicklern, die an einem Multi-MLoC-Projekt arbeiteten. Das ist ein großer Unterschied zu Hunderten von Entwicklern: Sie können nichts von Grund auf neu schreiben , weil Sie dafür Jahre brauchen würden. So sehr ich die Idee hasste, war eine Investition in eine verteilte Zusammenstellung wirtschaftlich machbar, eine Umschreibung nicht. Oh, und ich habe diese Schnittstellen geerbt. :-xIch war vor einem Jahrzehnt nicht dort, als sie ausgedacht wurden. (Ich habe viel von diesem Code geändert, um TMP zu verwenden, um mehr Fehler beim Kompilieren zu finden und weniger im Feld.)
sbi

2

Erstens, warum dauert das Kompilieren so lange?

  • Unterstützt Ihre Umgebung (IDE, make, was auch immer) inkrementelle Builds? Stellen Sie sicher, dass Sie nur die Änderungen neu kompilieren und nicht das Ganze.
  • Wenn Sie einen Multi-Core-Rechner haben, unterstützt Ihre IDE möglicherweise die parallele Kompilierung. Ich weiß, dass Visual Studio das macht. Anscheinend auch gcc. Holen Sie sich eine bessere Maschine und aktivieren Sie die parallele Kompilierung.
  • Erwägen Sie die Verwendung vorkompilierter Header.
  • Wenn Sie dies alles versuchen und die Kompilierung immer noch langsam ist, überprüfen Sie Ihren Code. Suchen Sie nach unnötigen Abhängigkeiten. Fügen Sie einen Header bei, in dem eine Forward-Deklaration ausreichend wäre? Ziehen Sie die Verwendung der PIMPL-ID in Betracht, um die Abhängigkeit von Headern zu verringern.

Wenn Ihre Erstellungszeit danach immer noch langsam ist, lösen Sie das Problem: Erstellen Sie viele kleine Testprojekte und bearbeiten Sie jedes einzeln. Stellen Sie sicher, dass Sie über ein automatisiertes nächtliches Build-System verfügen, das einen neuen Checkout durchführt, alles erstellt und alle Komponententests automatisch ausführt.

Wenn Sie immer noch viel Zeit benötigen, um Ihre Änderungen zu testen, sollten Sie sich eingehender mit ihnen befassen. Stellen Sie vor dem Testen sicher, dass Sie in Ihrem Versionskontrollsystem einen Unterschied machen und alle Änderungen sorgfältig prüfen. Kurz gesagt, ähnelt dies weitgehend der Entwicklung eingebetteter Systeme, bei der die Bearbeitungszeit für einen Test lang ist und Sie nur begrenzt in der Lage sind, den Status des Systems zu überprüfen.

Dies führt mich zu einem anderen Gedanken: Instrumentieren Sie Ihren Code, um die Protokollierung zu verwenden. Auf diese Weise können Sie möglicherweise das Problem erkennen, ohne ein Dutzend Mal neu erstellen und ausführen zu müssen.


2
Ich bin mir nicht sicher, ob GCC parallele Kompilierungen so gut unterstützt wie die Tatsache, dass make oder ähnliche Tools mehrere Kopien von GCC starten, wenn Sie es auch mitteilen.
Zachary K

1
+1 für PIMPL. Ich habe an einem Projekt gearbeitet, bei dem die Bauzeiten außer Kontrolle gerieten. In diesem Projekt habe ich nicht darauf geachtet, wie viele andere Header in jedem Header enthalten waren. Bei meinem nächsten Projekt war es mir ein Anliegen, dies durch den umfassenden Einsatz von PIMPL zu minimieren. Die Bauzeiten sind weiterhin großartig, obwohl das zweite Projekt wahrscheinlich doppelt so groß ist wie das erste.
Jason B

1

Sie benötigen wahrscheinlich einen mehrpoligen Ansatz:

1) Schnellere Build-Systeme. So viele Kerne / RAM / schnelle Festplatte, wie Sie sich leisten können. Bei größeren C ++ - Projekten ist die Festplatte häufig ein Begrenzer. Stellen Sie daher sicher, dass Sie schnelle haben.

2) Mehr Modularisierung des Projekts. Teilen Sie die Inhalte so auf, dass Änderungen nicht zu einer vollständigen Neukompilierung führen können. Schieben Sie ehrlich gesagt so viele grundlegende Dinge wie möglich in separate DLL / SO-Dateien, damit ein Teil des Projekts vollständig vom Rest getrennt werden kann.

3) Inkrementelle Builds / verteilte Builds / Caching entsprechend Ihrer Umgebung. Auf einigen Systemen kann durch distcc (verteiltes Erstellen) und ccache (Zwischenspeichern von teilweise erstellten Inhalten) viel Kompilierzeit gespart werden.

4) Stellen Sie sicher, dass Ihr Build gut parallelisiert werden kann. Insbesondere in einer Makefile-Umgebung ist es nicht schwer, in eine Situation zu geraten, in der Sie die Makefiles versehentlich so eingerichtet haben, dass Sie keine parallele Erstellung durchführen können.


0

Umfangreiche Protokollierung und interne Validierung waren für lange Durchlaufzeiten hilfreich. Sobald Ihr Build fertig ist, kann ein einzelner Durchlauf eine große Anzahl möglicher Probleme auf einmal aufdecken.

Bei komplexen Algorithmen oder der Buchhaltung kann es hilfreich sein, eine stark vereinfachte Version parallel zur "echten" Version einzufügen. In jedem Lauf sind nützliche Referenzdaten enthalten.


0

Was @sbi und @Michael Kohne gesagt haben.

Nehmen Sie sich Zeit und Energie für den Build-Prozess. Es war einmal ein stattliches, ausgereiftes Produkt, für dessen Herstellung mehr als eine Stunde benötigt wurde. Es wurde viel Zeit und Energie aufgewendet, um die angeblichen Build-Abhängigkeiten zu reparieren und später zu reduzieren, was sie tatsächlich waren. Die Build-Zeit ist auf ~ 30 Minuten gesunken.

Beim Ändern der Build-Tools wurde es mehr gelöscht. Bei einem mehrteiligen Projekt kann 'scons' alle Kompilierungen durchführen, bevor Verknüpfungen erstellt werden. 'make' mit mehreren Makefiles kompiliert ein einzelnes Projekt, bevor dieses Projekt verknüpft wird, und fährt dann fort.

Das brachte uns zu dem Punkt, dass alle einzelnen Kompilierbefehle massiv parallel ausgeführt werden konnten. 'distcc' auf langsamen Rechnern, make / scons -j8 auf Multicore-Rechnern. Das brachte volle Builds auf ein paar Minuten.

Erstellen Sie in einem anderen Licht einen automatisierten nächtlichen Erstellungsprozess. Auf diese Weise kann verhindert werden, dass mehrere Personen mehrere fehlgeschlagene Builds (erneut) ausführen, wenn Probleme in Ihrem Quell-Repository auftreten.

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.