GNU-Make

Paralleles Kompilieren von Code mit Make

Paralleles Kompilieren von Code mit Make

Wer auch immer Sie fragt, wie man Software richtig erstellt, wird Make als eine der Antworten finden. Auf GNU/Linux-Systemen ist GNU Make [1] die Open-Source-Version des ursprünglichen Makes, das vor mehr als 40 Jahren veröffentlicht wurde - 1976. Make funktioniert mit einem Makefile – einer strukturierten Klartextdatei mit diesem Namen, der am besten als Bauanleitung für den Softwareerstellungsprozess beschrieben werden kann. Das Makefile enthält eine Reihe von Labels (so genannte Targets) und die spezifischen Anweisungen, die ausgeführt werden müssen, um jedes Target zu erstellen.

Einfach gesagt, Make ist ein Build-Tool. Es folgt dem Rezept der Aufgaben aus dem Makefile. Es ermöglicht Ihnen, die Schritte automatisiert zu wiederholen, anstatt sie in ein Terminal einzugeben (und wahrscheinlich beim Tippen Fehler zu machen).

Listing 1 zeigt ein Beispiel-Makefile mit den beiden Zielen „e1“ und „e2“ sowie den beiden Spezialzielen „all“ und „clean“.” Ausführen von „make e1“ führt die Anweisungen für das Ziel „e1“ aus und erstellt die leere Datei one. Das Ausführen von „make e2“ macht dasselbe für das Ziel „e2“ und erstellt die leere Datei zwei. Der Aufruf von „make all“ führt die Anweisungen für das Ziel e1 first und e2 next aus. Um die zuvor erstellten Dateien eins und zwei zu entfernen, führen Sie einfach den Aufruf „make clean“ aus.”

Eintrag 1

alle: e1 e2
e1:
berühren
e2:
Berühre zwei
sauber:
rm eins zwei

Laufende Marke

Der übliche Fall ist, dass Sie Ihr Makefile schreiben und dann einfach den Befehl „make“ oder „make all“ ausführen, um die Software und ihre Komponenten zu erstellen. Alle Targets werden in serieller Reihenfolge und ohne Parallelisierung erstellt. Die Gesamtbauzeit ist die Summe der Zeit, die benötigt wird, um jedes einzelne Ziel zu bauen.

Dieser Ansatz funktioniert gut für kleine Projekte, dauert aber bei mittleren und größeren Projekten ziemlich lange. Dieser Ansatz ist nicht mehr zeitgemäß, da die meisten aktuellen CPUs mit mehr als einem Kern ausgestattet sind und die Ausführung von mehr als einem Prozess gleichzeitig ermöglichen. Vor diesem Hintergrund schauen wir uns an, ob und wie der Build-Prozess parallelisiert werden kann. Das Ziel ist einfach die Bauzeit zu verkürzen.

Verbesserungen machen

Es gibt ein paar Möglichkeiten, die wir haben - 1) den Code vereinfachen, 2) die einzelnen Aufgaben auf verschiedene Rechenknoten verteilen, den Code dort erstellen und das Ergebnis von dort sammeln, 3) den Code parallel auf einer einzigen Maschine erstellen und 4) Kombinieren Sie die Optionen 2 und 3.

Variante 1) ist nicht immer einfach. Es erfordert den Willen, die Laufzeit des implementierten Algorithmus zu analysieren und Kenntnisse über den Compiler, d.h.e., Wie übersetzt der Compiler die Anweisungen in der Programmiersprache in Prozessoranweisungen?.

Option 2) erfordert den Zugriff auf andere Rechenknoten, z. B. dedizierte Rechenknoten, ungenutzte oder weniger genutzte Maschinen, virtuelle Maschinen von Cloud-Diensten wie AWS oder gemietete Rechenleistung von Diensten wie LoadTeam [5]. In Wirklichkeit wird dieser Ansatz verwendet, um Softwarepakete zu erstellen. Debian GNU/Linux verwendet das sogenannte Autobuilder-Netzwerk [17] und RedHat/Fedors verwendet Koji [18]. Google nennt sein System BuildRabbit und wird im Talk von Aysylu Greenberg perfekt erklärt [16]. distcc [2] ist ein sogenannter verteilter C-Compiler, mit dem Sie Code auf verschiedenen Knoten parallel kompilieren und Ihr eigenes Build-System aufbauen können.

Option 3 verwendet Parallelisierung auf lokaler Ebene. Dies ist möglicherweise die Option mit dem besten Preis-Leistungs-Verhältnis für Sie, da keine zusätzliche Hardware wie bei Option 2 erforderlich ist. Voraussetzung für die parallele Ausführung von Make ist das Hinzufügen der Option -j im Aufruf (kurz für -jobs). Dies gibt die Anzahl der Jobs an, die gleichzeitig ausgeführt werden. Die folgende Auflistung fordert Make auf, 4 Jobs parallel auszuführen:

Eintrag 2

$ machen --jobs=4

Nach dem Gesetz von Amdahl [23] verkürzt sich dadurch die Bauzeit um fast 50 %. Beachten Sie, dass dieser Ansatz gut funktioniert, wenn die einzelnen Ziele nicht voneinander abhängen; zum Beispiel ist die Ausgabe von Ziel 5 nicht erforderlich, um Ziel 3 zu erstellen.

Allerdings gibt es einen Nebeneffekt: Die Ausgabe der Statusmeldungen für jedes Make-Target erscheint willkürlich und diese lassen sich nicht mehr eindeutig einem Target zuordnen. Die Ausgabereihenfolge hängt von der tatsächlichen Reihenfolge der Auftragsausführung ab.

Ausführungsreihenfolge erstellen definieren

Gibt es Aussagen, die Make helfen zu verstehen, welche Ziele voneinander abhängen?? Ja! Das Beispiel-Makefile in Listing 3 sagt folgendes:

* Um das Ziel „all“ zu erstellen, führen Sie die Anweisungen für e1, e2 und e3 aus

* Ziel e2 erfordert, dass Ziel e3 vorher gebaut wurde

Dies bedeutet, dass die Ziele e1 und e3 parallel gebaut werden können, zuerst folgt e2, sobald der Bau von e3 abgeschlossen ist, schließlich.

Auflistung 3

alle: e1 e2 e3
e1:
berühren
e2: e3
Berühre zwei
e3:
Berühre drei
sauber:
rm eins zwei drei

Visualisieren Sie die Make-Abhängigkeiten

Das clevere Tool make2graph aus dem Projekt makefile2graph [19] visualisiert die Make-Abhängigkeiten als gerichteten azyklischen Graphen. Dies hilft zu verstehen, wie die verschiedenen Ziele voneinander abhängen. Make2graph gibt Diagrammbeschreibungen im Punktformat aus, die Sie mit dem Punktbefehl aus dem Graphviz-Projekt [22] in ein PNG-Bild umwandeln können. Der Aufruf lautet wie folgt:

Auflistung 4

$ alles machen -Bnd | make2graph | Punkt -Tpng -o Grafik.png

Zuerst wird Make mit dem Ziel „all“ aufgerufen, gefolgt von den Optionen „-B“, um bedingungslos alle Ziele zu erstellen, „-n“ (kurz für „-dry-run“), um so zu tun, als würden die Anweisungen pro Ziel ausgeführt, und „ -d“ („-debug“), um Debug-Informationen anzuzeigen. Die Ausgabe wird an make2graph weitergeleitet, das seine Ausgabe an einen Punkt weiterleitet, der den Graphen der Bilddatei generiert.png im PNG-Format.


Das Build-Abhängigkeitsdiagramm für das Listing 3

Mehr Compiler und Build-Systeme

Wie bereits oben erläutert, wurde Make vor mehr als vier Jahrzehnten entwickelt. Im Laufe der Jahre hat die parallele Ausführung von Jobs immer mehr an Bedeutung gewonnen und die Zahl speziell entwickelter Compiler und Build-Systeme, um einen höheren Parallelisierungsgrad zu erreichen, ist seitdem gewachsen. Die Liste der Werkzeuge umfasst diese:

Die meisten von ihnen wurden mit Blick auf die Parallelisierung entwickelt und bieten ein besseres Ergebnis in Bezug auf die Build-Zeit als Make.

Fazit

Wie Sie gesehen haben, lohnt es sich, über parallele Builds nachzudenken, da dies die Build-Zeit bis zu einem bestimmten Level erheblich verkürzt. Dennoch ist es nicht einfach zu erreichen und birgt einige Fallstricke [3]. Es wird empfohlen, sowohl Ihren Code als auch seinen Build-Pfad zu analysieren, bevor Sie mit parallelen Builds beginnen.

Links und Referenzen

Battle for Wesnoth-Tutorial
The Battle for Wesnoth ist eines der beliebtesten Open-Source-Strategiespiele, die Sie derzeit spielen können. Dieses Spiel befindet sich nicht nur se...
0 A.D. Lernprogramm
Von den vielen Strategiespielen da draußen, 0 A.D. schafft es, sich trotz Open Source als umfassender Titel und sehr tiefgehendes, taktisches Spiel ab...
Unity3D-Tutorial
Einführung in Unity 3D Unity 3D ist eine leistungsstarke Engine für die Spieleentwicklung. Es ist plattformübergreifend, das heißt, Sie können Spiele ...