Codice di compilazione in parallelo usando Make

Codice di compilazione in parallelo usando Make

Chiunque tu chieda come costruire correttamente il software verrà visualizzato come una delle risposte. Sui sistemi GNU/Linux, GNU Make [1] è la versione open source della marca originale che è stata rilasciata più di 40 anni fa - nel 1976. Fai lavori con un makefile: un file di testo semplice strutturato con quel nome che può essere meglio descritto come manuale di costruzione per il processo di costruzione del software. Il makefile contiene un numero di etichette (chiamate target) e le istruzioni specifiche devono essere eseguite per costruire ciascun target.

Semplicemente parlando, make è uno strumento di costruzione. Segue la ricetta dei compiti dal makefile. Ti consente di ripetere i passaggi in modo automatizzato piuttosto che digitarli in un terminale (e probabilmente commettere errori durante la digitazione).

La lista 1 mostra un esempio di esempio con i due obiettivi "E1" ed "E2", nonché i due obiettivi speciali "All" e "Clean."L'esecuzione di" Make E1 "esegue le istruzioni per il target" E1 "e crea il file vuoto. L'esecuzione di "make e2" fa lo stesso per il target "e2" e crea il file vuoto due. La chiamata di "Make All" esegue le istruzioni per Target E1 First ed E2 Avanti. Per rimuovere i file precedentemente creati uno e due, eseguire semplicemente la chiamata "Rendi pulita."

Elenco 1

Tutti: E1 E2
E1:
Tocca uno
E2:
Tocca due
pulito:
rm uno due

Running Make

Il caso comune è che scrivi il tuo makefile e quindi esegui il comando "make" o "make tutto" per costruire il software e i suoi componenti. Tutti gli obiettivi sono costruiti in ordine seriale e senza alcuna parallelizzazione. Il tempo di costruzione totale è la somma del tempo necessario per costruire ogni singolo obiettivo.

Questo approccio funziona bene per piccoli progetti ma richiede molto tempo per progetti medi e più grandi. Questo approccio non è più aggiornato poiché la maggior parte delle CPU attuali sono dotate di più di un core e consentono l'esecuzione di più di un processo alla volta. Con queste idee in mente, guardiamo se e come il processo di costruzione può essere parallelizzato. L'obiettivo è semplicemente ridurre il tempo di costruzione.

Apportare miglioramenti

Ci sono alcune opzioni che abbiamo - 1) semplificare il codice, 2) distribuire le singole attività su diversi nodi di calcolo, creare il codice lì e raccogliere il risultato da lì, 3) Crea il codice in parallelo su una singola macchina e 4) Combina le opzioni 2 e 3.

L'opzione 1) non è sempre facile. Richiede la volontà di analizzare il runtime dell'algoritmo e la conoscenza implementati sul compilatore, i.e., In che modo il compilatore traduce le istruzioni nel linguaggio di programmazione in istruzioni per il processore.

Opzione 2) Richiede l'accesso ad altri nodi di elaborazione, ad esempio nodi di elaborazione dedicati, macchine inutilizzate o meno usate, macchine virtuali da servizi cloud come AWS o potenza di calcolo noleggiate da servizi come LoadTeam [5]. In realtà, questo approccio viene utilizzato per creare pacchetti software. Debian GNU/Linux utilizza la cosiddetta rete Autobuilder [17] e Redhat/Feders utilizza Koji [18]. Google chiama il suo sistema buildrabbit ed è perfettamente spiegato nel discorso di Aysylu Greenberg [16]. Discc [2] è un cosiddetto compilatore C distribuito che consente di compilare il codice su diversi nodi in parallelo e di impostare il proprio sistema di build.

L'opzione 3 utilizza la parallelizzazione a livello locale. Questa potrebbe essere l'opzione con il miglior rapporto costi-benefici per te, in quanto non richiede hardware aggiuntivo come nell'opzione 2. Il requisito di eseguire Make in Parallel sta aggiungendo l'opzione -j nella chiamata (abbreviazione di -jobs). Ciò specifica il numero di lavori che vengono eseguiti contemporaneamente. L'elenco di seguito chiede di svolgere 4 posti di lavoro in parallelo:

Elenco 2

$ make --Jobs = 4

Secondo la legge di Amdahl [23], ciò ridurrà il tempo di costruzione di quasi il 50%. Tieni presente che questo approccio funziona bene se i singoli obiettivi non dipendono l'uno dall'altro; Ad esempio, l'output di Target 5 non è richiesto per creare Target 3.

Tuttavia, esiste un effetto collaterale: l'output dei messaggi di stato per ciascun obiettivo sembra arbitrario e questi non possono più essere chiaramente assegnati a un target. L'ordine di output dipende dall'ordine effettivo dell'esecuzione del lavoro.

Definire Esegui l'ordine di esecuzione

Ci sono dichiarazioni che aiutano a capire quali obiettivi dipendono l'uno dall'altro? SÌ! L'esempio makefile nella lista 3 dice questo:

* Per creare target "All", eseguire le istruzioni per E1, E2 ed E3

* Target E2 richiede che il target E3 venga costruito prima

Ciò significa che gli obiettivi E1 ed E3 possono essere costruiti in parallelo, prima, quindi E2 segue non appena la costruzione di E3 è completata, infine.

Elenco 3

Tutti: E1 E2 E3
E1:
Tocca uno
E2: E3
Tocca due
E3:
Tocca tre
pulito:
rm uno due tre

Visualizza le dipendenze per fare

Lo strumento intelligente Make2Graph dal progetto Makefile2Graph [19] visualizza le dipendenze per fare come grafico aciclico diretto. Questo aiuta a capire come dipendono i diversi obiettivi reciproci. Make2Graph Output Descrizioni del grafico in formato punto che puoi trasformare in un'immagine PNG usando il comando DOT dal progetto Graphviz [22]. La chiamata è la seguente:

Elenco 4

$ Make All -bnd | make2graph | Dot -tpng -o grafico.png

In primo luogo, la creazione viene chiamata con il bersaglio "tutto" seguito dalle opzioni "-b" per costruire incondizionatamente tutti gli obiettivi, "-n" (abbreviazione di "-dry-run") per fingere di eseguire le istruzioni per target e " -d "(" -debug ") per visualizzare le informazioni di debug. L'output viene convogliato per creare2Graph che dà il suo output su punti che genera il grafico dei file di immagine.PNG in formato PNG.


Il grafico della dipendenza da build per l'elenco 3

Più compilatori e sistemi di costruzione

Come già spiegato sopra, Make è stato sviluppato più di quattro decenni fa. Nel corso degli anni, l'esecuzione di posti di lavoro in parallelo è diventato sempre più importante e da allora è cresciuto il numero di compilatori appositamente progettati e sistemi di costruzione per ottenere un livello più elevato di parallelizzazione. L'elenco degli strumenti include questi:

  • Bazel [20]
  • CMAKE [4]: ​​Abbreviates make e crea i file di descrizione successivamente utilizzati da Make
  • distmake [12]
  • Distributed Make System (DMS) [10] (sembra essere morto)
  • dmake [13]
  • LSF Make [15]
  • Apache Maven
  • Meson
  • Ninja Build
  • Nmake [6]: crea Microsoft Visual Studio
  • Pydoit [8]
  • Qmake [11]
  • ripetere [14]
  • Scons [7]
  • WAF [9]

La maggior parte di essi è stata progettata pensando alla parallelizzazione e offre un risultato migliore per quanto riguarda il tempo di costruzione rispetto a quello di fare.

Conclusione

Come hai visto, vale la pena pensare a build parallele in quanto riduce significativamente il tempo di costruzione fino a un certo livello. Tuttavia, non è facile da raggiungere e viene fornito con alcune insidie ​​[3]. Si consiglia di analizzare sia il codice che il suo percorso di build prima di entrare in build parallele.

Collegamenti e riferimenti

  • [1] GNU Make Manual: Parallel Execution, https: // www.gnu.org/software/make/manuale/html_node/parallelo.html
  • [2] DISCC: https: // github.com/distCC/distCC
  • [3] John Graham-Cumming: le insidie ​​e i benefici della GNU rendono parallelizzazione, https: // www.cmcrossroads.com/articolo/insidie ​​e benefici-gener-parallelizzazione
  • [4] Cmake, https: // cmake.org/
  • [5] LoadTeam, https: // www.LoadTeam.com/
  • [6] nmake, https: // docs.Microsoft.com/en-us/cpp/build/reference/nmake-reference?Visualizza = msvc-160
  • [7] Scons, https: // www.Scons.org/
  • [8] Pydoit, https: // pydoit.org/
  • [9] Waf, https: // gitlab.com/ita1024/waf/
  • [10] Distributed Make System (DMS), http: // www.non mongo.org/dms/indice.html
  • [11] Qmake, https: // doc.Qt.io/qt-5/qmake-manual.html
  • [12] distmake, https: // sourceforge.net/progetti/distmake/
  • [13] Dmake, https: // docs.oracolo.com/cd/e19422-01/819-3697/dmake.html
  • [14] RIDO, https: // redo.PRIEDTHOCS.io/en/ultimo/
  • [15] LSF Make, http: // Sunray2.MIT.EDU/KITS/Platform-LSF/7.0.6/1/GUIDES/KIT_LSF_GUIDE_SOURCE/PRINT/LSF_MAKE.PDF
  • [16] Aysylu Greenberg: Costruire un sistema di build distribuito su Google Scale, Goto Conference 2016, https: // gotocon.com/dl/goo-chicago-2016/slides/aysylugreenberg_buildingadistributedbuildsystestemyatglescalecalecale.PDF
  • [17] Debian Build System, Autobuilder Network, https: // www.Debian.org/sviluppo/buildd/indice.en.html
  • [18] Koji - RPM Building and Tracking System, https: // Pagure.io/koji/
  • [19] Makefile2Graph, https: // github.com/lindenb/makefile2graph
  • [20] Bazel, https: // bazel.costruire/
  • [21] Makefile Tutorial, https: // MakefileTorial.com/
  • [22] Graphviz, http: // www.Graphviz.org
  • [23] Amdahl's Law, Wikipedia, https: // en.Wikipedia.org/wiki/amdahl%27s_law