C/C++, vorkompilierte Dateien?



  • Hallo zusammen

    Da unser Build-Prozess relativ lange dauert hatte ich die Idee, diesen mal unter die Lupe zu nehmen. Nun habe ich dabei entdeckt, dass unter C/C++-Entwickler dort drin überall "make clean" (statt nur "make") drin hat, was meines Wissens heisst dass sämtliche vorkompilierte Dateien im Projektverzeichnis gelöscht werden.

    Er hat gemeint dass dies so sicherer sei, da es offenbar vorkommen kann dass sich geänderter Quelltext nicht richtig kompiliert... ich bin da anderer Meinung, dass nur die vorkompilierten Dateien neu erstellt werden von welchen der Quelltext sich geändert hat.

    Nun dachte ich mal bei euch nachzufragen, was nun "Stand der Dinge ist".

    Und: Falls ich damit recht hatte, dass nur vorkompilierte Dateien neu erstellt werden von denen sich der Quelltext geändert hat: Wie merkt der C++-Compiler (Linker?) dass der Quelltext nicht mehr zur vorkompilierten Datei (respektive ungekehrt) passt? Wird das über den Timestamp gemessen?

    Oder ist meine Sichweise nur korrekt, wenn man eine Entwicklungsumgebung ("IDE") statt z.B. nur GCC unter der Konsole verwendet?

    Bin da ehrlich gesagt ziemlich ratlos bei diesen Themen. Ich denke aber, wenn man statt "make clean" nur "make" verwenden würde, dass es dann schneller geht. Nur muss es auch zuverlässig sein...

    Vielen Dank für die Feedbacks.

    Mit freundlichem Grüssen,
    Jan


  • Mod

    Make funktioniert schon absolut zuverlässig und 100% gemäß seiner Dokumentation. Und seine Verhaltensweisen sind auch absolut sinnvoll. Gegeben, dass man sich denn auskennt und all diese Dinge weiß. Wenn man sich nicht gut auskennt, kann es schon gut vorkommen, dass da Dinge passieren, die man nicht erwartet hätte. Ein typischer Fehler ist zum Beispiel, dass man in C++ gerne Dinge in Headern implementiert, aber die Headerdateien nicht im Makefile als Abhängigkeiten der eigentlichen Quelldateien einträgt. Dann kann so etwas passieren, was euer Entwickler beobachtet hat.



  • @jmar83
    Also... grundsätzlich...
    Das was @SeppJ geschrieben hat stimmt grundsätzlich.
    Allerdings werden wirklich oft Dinge übersehen. Nicht nur bei den Dependencies, sondern z.B. auch beim Erzeugen der Output-Files. Denn dabei muss man sicherstellen dass kein Szenario existiert in dem ein Output-File mit aktuellem Datum erstellt wurde, dieses Output-File aber nicht vollständig ist. (Oder man muss diese Szenarien anders abdecken, z.B. mit zusätzlichen Dummy-Dependencies.)

    Beispiel: Man hat ein "ganz normales" Makefile, und einen Compiler der die .o Files direkt mit dem angegebenen Filenamen schreibt. Also ohne den "Output in ein Temp-File schreiben und dann umbenennen" Trick. Jetzt startet man einen Build und dieser wird mittendrin unterbrochen. z.B. wegen

    • Stromausfall
    • Systemcrash wegen Treiberbug
    • Out of Memory und der OOM-Killer killt den Compiler weg
    • Ctrl+C
      ...

    Dabei kann es passieren dass der Compiler unterbrochen wird nachdem er das Output-File geöffnet hat, aber noch bevor er es fertig "befüllt" hat. Dummerweise hat das Output-File danach aber das aktuelle Datum, und make erkennt daher beim nächsten Start nicht dass es neu gebaut werden muss. Wenn du Glück hast bekommst du dabei Linker-Fehler. Wenn du Pech hast bekommst du ohne Fehler ein Executable raus, das dann aber nicht das tut was es tun soll.

    Das selbe kann natürlich beim Linken passieren, bzw. überall dort wo Output-Files erzeugt werden deren "Aktualität" von make anhand des Datums festgestellt wird.

    Man kann das ganz natürlich fixen, aber um 100% sicher zu sein muss man wirklich jede einzelne Rule im Makefile analysieren und gucken ob dabei so ein Problem entstehen kann.


    Was oft ein guter Kompromiss zu inkrementellen Builds ist ist CCache. Damit könnt ihr das "make clean" beibehalten, und Builds wo sich nicht viel geändert hat werden trotzdem ordentlich viel schneller. Siehe https://ccache.dev/

    Wie man CCache installiert ist nicht in 1-2 Sätzen erklärt, aber es ist auch nicht wirklich sehr schwer/aufwendig. Falls es dich interessiert lässt sich das relativ einfach ergoogeln.

    Wichtig zu wissen wäre dabei vielleicht dass du CCache auf verschiedene Arten einbinden kannst: über das Makefile oder über das Verbiegen von Symlinks/Aliases auf dem Build-Rechner. Die erste Variante eignet sich gut wenn man immer und überall mit CCache bauen will (muss dann aber natürlich auch überall installiert sein). Die zweite eignet sich gut wenn man CCache auf einem PC für sämtliche C und/oder C++ Builds verwenden will, ohne dass man die Makefiles anpassen muss.



  • @SeppJ sagte in C/C++, vorkompilierte Dateien?:

    Make funktioniert schon absolut zuverlässig und 100% gemäß seiner Dokumentation.

    Das liest doch niemand. Ist doch viel einfacher in irgendeinem x-beliebigem Forum zu fragen.



  • Schreibt ihr denn das Makefile selber oder nutzt ihr einen Generator wie cmake? Als ich noch das Makefile von Hand geschrieben habe, musste ich auch oft make clean laufen lassen, weil man einfach zu leicht vergessen kann, das Makefile mit anzupassen.



  • Also bei offiziellen Builds, die dann auch ausgeliefert werden, mache ich vorher auch ein clean ( also mein Buildscript macht das ), eben wegen der bereits beschriebenen möglichen Verwirrungen. Sicher ist sicher.

    Während der Entwicklung mache ich seltener clean, weil eben ein kompletter Rebuild zu lang dauert.

    Von welchen Build-Laufzeiten reden wir denn hier?

    Du klingst jetzt nicht unbedingt so, als hättest du selber Software-Entwicklungs-Hintergrund. Warum willst du deinem Entwickler reinreden?



  • Hallo zusammen

    Vielen Dank für die hoch kompetenten Feedbacks!!

    Mit freundlichen Grüssen,
    Jan


  • Mod

    @wob sagte in C/C++, vorkompilierte Dateien?:

    Schreibt ihr denn das Makefile selber oder nutzt ihr einen Generator wie cmake? Als ich noch das Makefile von Hand geschrieben habe, musste ich auch oft make clean laufen lassen, weil man einfach zu leicht vergessen kann, das Makefile mit anzupassen.

    Teils teils. Vergessen, das Makefile anzupassen ist genau der Grund, wieso man es nicht selber schreiben sollte. Andererseits bin ich mit Makefiles so viel besser als mit den Makefilegeneratoren, dass die Benutzung einer Autotoolchain ein für mich nicht unerheblicher Mehraufwand ist. Daher wäge ich halt ab, was ich für den insgesamt kleineren Aufwand halte: Makefile selber schreiben, bei Projektanderungen nachziehen, und abundzu frustrierende Fehler haben, weil ich dies vergessen habe; gegenüber dem Mehraufwand mit einer Autogenerierung, wo größere Änderungen auch nicht trivial sind (kleine aber schon), und wo ich tendenziell mehr Fehler mache.

    Das Wichtigste aber zum Schluss: Wenn ich auf Portabilität achten muss, es also nicht nur mein Privatspaß ist, führt an einer Autogenerierung wenig vorbei.



  • Ich kämpfe auch eher gegen CMake als dass ich mit CMake arbeite.

    Da ich mich aber auch mit Makefiles nicht sonderlich auskenne und CMake mir Out-of-Source-Builds bietet und auch z.B. automatisch mit CPack ein .deb bauen kann, beiße ich dann doch in den Apfel mit negativem pH-Wert und stümpere mir eine CMakeLists.txt zusammen 😱



  • Irgendwie bekomme ich keine Mails bei Antworten, dies obwohl "Beobachtet" ausgewählt wurde. Oder hat dies nix mit Mails zu tun...? Vielen Dank für die Feedbacks.

    Mit freundlichen Grüssen,
    Jan


  • Administrator

    @jmar83 sagte in C/C++, vorkompilierte Dateien?:

    Irgendwie bekomme ich keine Mails bei Antworten, dies obwohl "Beobachtet" ausgewählt wurde. Oder hat dies nix mit Mails zu tun...? Vielen Dank für die Feedbacks.

    Ob rechts auf dein Icon klicken, auf Einstellungen gehen und dort kannst du einstellen, bei welchen Benachrichtigungen du eine E-Mail bekommst. Standardmässig werden keine E-Mails verschickt.



  • Vielen Dank, das scheint wohl geklappt zu haben. (Bei der nächsten Antwort werde ich sehen, ob dann ein Mail reinkommt...)

    Zurück zum Thema:

    • Mit der originalen Geschwindigkeit des Raspiberry Pi 3B+ (=4x 1.4Ghz) ging das Kompilieren ca. 1 Std. und 10 Min.
    • Nachdem Übertakten auf 4x 1.55Ghz meldete der Compiler mittendrin einen "segfault"...
    • Nach dem Anpassen der Übertaktung von 4x 1.55GHz auf 4x 1.5Ghz war der "segfault" weg und es dauerte noch ca. 1 Stunde
    • Nach der Installation von "ccache" und bei der ersten Kompilation ging's weniger als 1 Stunde. (Schon bei der ersten Kompilation mit "ccache" gab's wohl irgendwelche "Überschneidungen" - es werden insgesamt 7 Sachen kompiliert, 3 Libraries (zuerst) und dann anschliessend noch 4 ausführbare Anwendungen)
    • Nach der 2. Kompilation mit "ccache" dauerte es noch ca. 5.5 Minuten!!!!! 🙂

    -> Die Tipps zum Übertakten habe ich von hier: https://miguelangelantolin.com/raspberry-pi-3-b-overclocking/ (-> Einfach "1550" mit "1500" ersetzen!!)

    -> "ccache" aufzusetzen war auch total easy... so wie es hier beschrieben wird, funktioniert's auch bei Raspbian Stretch (aktuelle Version): https://askubuntu.com/questions/470545/how-do-i-set-up-ccache

    "Du klingst jetzt nicht unbedingt so, als hättest du selber Software-Entwicklungs-Hintergrund. Warum willst du deinem Entwickler reinreden?"

    ...bin auch Entwickler, zumindest so halbwegs (nebenbei Projektleitung und auch noch zuständig für die interne IT, quasi "Junge für alles" halt..;-)), allerdings komme ich eher aus der Richtung Java, PHP & .NET (Der Server-Teil von Webapps halt, GUI-Sachen sind jedoch weniger "meins") wie auch SQL.

    Nebenbei kenne ich mich noch mit Shell-/Bashskripts unter Linux aus, und das aktuelle Thema ist ein Skript, welches von "git pull" übers Kompilieren (C/C++-Hintergrunddienste) zum Packen (.deb-Datei) hin bis zur Verteilung auf's APT-Repo alles macht. (das Paket "reprepro" kann ich dafür wärmstens empfehlen -> https://blog.packagecloud.io/eng/2017/03/23/create-debian-repository-reprepro/ )

    Deshalb war ich auch mit der Kompilierungszeit der C/C++-Sachen beschäftigt. Wir haben uns zuerst überlegt, meine Skripte konfigurierbar zu machen (also so dass nicht immer alle 7 "targets" kompiliert werden müssen, etwa über Parameter), nun scheint der ganze Prozess max. 20 Minuten zu dauern - also kein Problem, um immer alles zu kompilieren!! 🙂

    Mit freundlichen Grüssen,
    Jan


  • Mod

    Heißer Tipp: Compilier nicht auf einem Raspberry Pi, sondern für einen Raspberry Pi. Auf einem normalen Desktoprechner sollte das um ein Vielfaches schneller gehen. Crosscompiling (wie man das nennt) ist zwar nicht ganz einfach und wird bei den ersten Versuchen bestimmt oft schief gehen, aber für so eine beliebte Plattform wirst du bestimmt viele Anleitungen oder gar fertige Werkzeuge finden. Ich brauchte bei Google bloß 'cros' einzugeben und es wurde mir schon 'cross compiling raspberry pi' vorgeschlagen. Ich bin also wohl nicht der Erste mit dieser Suchanfrage 🙂



  • Vielen Dank für den Tipp. Daran habe ich auch schon gedacht, aber die ganze Konfiguration hat mich vom Thema abgehalten.

    Na ja, vielleicht ist es ja doch nicht soooooo schwierig, werde es wohl bei der nächsten Gelegenheit ausprobieren. (Aber aktuell hab ich der Belegschaft und dem Chef schon mitgeteilt, dass mein Projekt (im Grossen und Ganzen) fertig ist und es nun genutzt werden kann..)

    P.S.: Nun klappt's auch mit der Mail-Benachrichtigung wenn jemand im Forum hier antwortet - ebenfalls vielen Dank!



  • @jmar83 sagte in C/C++, vorkompilierte Dateien?:

    • Nachdem Übertakten auf 4x 1.55Ghz meldete der Compiler mittendrin einen "segfault"...
    • Nach dem Anpassen der Übertaktung von 4x 1.55GHz auf 4x 1.5Ghz war der "segfault" weg und es dauerte noch ca. 1 Stunde
    • Nach der 2. Kompilation mit "ccache" dauerte es noch ca. 5.5 Minuten!!!!! 🙂

    Ich würde die Übertaktung wieder rückgängig machen. Nicht jeder Fehler den die CPU bei zu hohem Takt macht muss in nem SIGSEGV enden. Wenns blöd hergeht erzeugt das Ding ein Programm das auch anscheinend das macht was es soll, aber dann irgendwo doch nicht richtig funktioniert. Nachdem der Gewinn durch Übertaktung sowieso so gering ist, ... wäre es mir das Risiko nicht wert.



  • "Ich würde die Übertaktung wieder rückgängig machen. Nicht jeder Fehler den die CPU bei zu hohem Takt macht muss in nem SIGSEGV enden."

    Bin bin es eigentlich gewöhnt, dass dabei der Rechner einfriert! 😉 (Das Problem mit diesem Fehler hängt aber 100% mit der Übertaktung zusammen, vorher ging's x mal ständig gut...)

    "Nachdem der Gewinn durch Übertaktung sowieso so gering ist, ... wäre es mir das Risiko nicht wert."

    Volle Zustimmung, jetzt mit dem "ccache" geht's ja ohnehin ab wie die Post! 🙂



  • @jmar83 sagte in C/C++, vorkompilierte Dateien?:

    "Ich würde die Übertaktung wieder rückgängig machen. Nicht jeder Fehler den die CPU bei zu hohem Takt macht muss in nem SIGSEGV enden."

    Bin bin es eigentlich gewöhnt, dass dabei der Rechner einfriert! 😉 (Das Problem mit diesem Fehler hängt aber 100% mit der Übertaktung zusammen, vorher ging's x mal ständig gut...)

    Hi! Ich bin hier zwar etwas spät dran, aber das ist wohl ein Problem das sich ohnehin nicht so schnell in Luft auflöst.

    Scheinbar wurde hier im Thread noch nicht erwähnt, dass sich make mit der Option -j<N> auch parallel betreiben lässt. Das würde ich zuerst versuchen, bevor du solche abenteuerlichen Sachen wie "CPU übertakten" machst. Versuch es erstmal mit make -j8 für eine 4-Kern-CPU mit Hyperthreading.

    Es gibt kaum etwas, das so gut von massiver Parallelisierung profitiert wie die meisten Build-Prozesse. Abhängigkeitsprobleme in den Makefiles sind hier allerdings kontraproduktiv und können den Parallel-Build auch unmöglich machen. Einen Versuch sollte es dennoch wert sein.



  • Hallo Finnegan

    Danke für Deinen Beitrag!

    Da das Raspi (3B+) zwar 4 Kerne hat jedoch kein Hyper-Threading (gibt's doch nur bei x86/x64/PC-kompatiblen CPUs?) habe ich mit der Option "-j4" kompiliert. Scheint zu klappen.

    (Hyper-Threading macht auf einen phys. Kern 2 virtuelle, so habe ich es zumindest im Windows-Gerätemanager beobachtet, bin mir aber noch 100% sicher ob meine Vorstellung stimmt?)

    Ob ein Zuwachs an Geschwindigkeit gegeben ist, sehen ich am Schluss des Build-Prozess. (Zeitdifferenz messen im Shell-Skript)

    Aktuell bin ich immer noch beim Raspi geblieben (statt cross-compile auf einem PC), da mir "ccache" die ganze Sache extrem beschleunigt hat.

    Ich schreibe dann noch mal einen Beitrag wenn es fertig ist! 🙂



  • Super, hat geklappt, Ziel erreicht - vielen vielen Dank!!

    Nun bin ich bei 17 Minuten statt bei 19-20 Minuten wie vorher.

    Wobei eh ein grosser Teil der Zeit gebraucht wird um das .deb-File zu packen (dpkg -b ...), aber dazu habe ich gelesen dass "dpkg-deb" (statt "dpkg") es zulassen würde die Kompressionsmethode sowie -stufe anzupassen. Na ja, das Packen (build) und das Entpacken (install) wäre damit schneller, die Datei allerdings grösser zum downloaden. Man muss halb abwägen...

    Und, nur so nebenbei: Die Option "--force-unsafe" bei "dpkg" scheint auch nicht viel zu bringen, ist evtl. nur zum Installieren geeignet statt zum Builden per "dpkg"... Warnung (in Kombination mit dem Parameter "-b") bekomme ich aber keine...



  • @jmar83 Kann es sein dass du ein tipp-fehler hast? Denn --force-unsafe gibt es nicht als parameter für dpkg. Meintest du eher --force-unsafe-io?

    https://wiki.debian.org/Teams/Dpkg/FAQ#Q:_Why_is_dpkg_so_slow_when_using_new_filesystems_such_as_btrfs_or_ext4.3F


Anmelden zum Antworten