Beschleunigte Befehlsbearbeitung mit GNU Parallel

Aus LinuxUser 06/2013

Beschleunigte Befehlsbearbeitung mit GNU Parallel

© bambamstiger, Fotolia

Multiple Persönlichkeit

Durch den geschickten Einsatz des pfiffigen kleinen Programms GNU Parallel reizen Sie auch in Skripten die Multicore-CPU Ihres Rechners voll aus.

Wer aus dem Urlaub kommt, der bringt in der Regel auch unzählige Schnappschüsse mit. Um die Auflösung der Fotos für die Web-Galerie zu reduzieren, genügt schon Mogrify aus dem ImageMagick-Paket und folgender Einzeiler:

$ for i in *.tif; do mogrify -resize 50% $i; done

Der Befehl durchläuft alle Dateien mit der Endung .tif im aktuellen Verzeichnis (for in *.tif) und lässt die Auflösung dann jeweils von Mogrify um die Hälfte reduzieren (mogrify -resize 50%). Da der Befehl jedoch die Dateien brav nacheinander abarbeitet, läuft in modernen Prozessoren ein Rechenkern auf Hochtouren, während die anderen Däumchen drehen. Wesentlich effektiver und vor allem schneller ginge es, könnte man mehrere Bilder gleichzeitig umwandeln. Genau hier springt das kleine und zu unrecht etwas missachtete Werkzeuge GNU Parallel ein.

Falscher Zwilling

Obwohl GNU Parallel bereits seit 2010 offiziell zur GNU-Werkzeugsammlung gehört [1], muss man es durchweg über den Paketmanager nachinstallieren. In vielen großen Distributionen fehlt es sogar komplett, so etwa in OpenSuse 12.2. Zu allem Übel gehört auch zum Paket moreutils [2] ein Programm namens Parallel. Das hat jedoch mit dem hier vorgestellten GNU Parallel nichts gemein.

Achten Sie daher bei der Installation darauf, das Paket für GNU Parallel zu erwischen. Finden Sie es nicht via Paketmanager, müssen Sie GNU Parallel selbst übersetzen. Dafür benötigen Sie lediglich Make, einen C-Compiler sowie das aktuelle Quellcode-Archiv [3]. Letzteres entpacken Sie und installieren GNU Parallel dann mittels ./configure && make && make install.

Sobald GNU Parallel auf der Festplatte liegt, prüfen Sie damit zur Probeweise die zwei Rechner gnu.org und linux-user.de gleichzeitig auf ihre Erreichbarkeit:

$ parallel ping -c2 ::: gnu.org linux-user.de

GNU Parallel startet hier zwei Mal den Befehl ping -c2, wobei es den ersten auf gnu.org ansetzt, den zweiten hingegen auf linux-user.de. Die beiden Programme laufen dabei gleichzeitig. Antwortet linux-user.de schneller als der GNU-Server, sehen Sie folglich auch zuerst seine Ausgabe (Abbildung 1).

Die drei Doppelpunkte im Kommando gehören zu GNU Parallel und trennen den zu startenden Befehl von den zu nutzenden Parametern. Parallel spricht übrigens nicht von gestarteten Befehlen oder Programmen, sondern allgemein von “auszuführenden Jobs”.

Abbildung 1: Der Rechner <code srcset=

192.168.2.11 im lokalen Netz antwortet hier schneller, als sein Kollege gnu.org aus dem Internet.” width=”300″ height=”214″ /> Abbildung 1: Der Rechner 192.168.2.11 im lokalen Netz antwortet hier schneller, als sein Kollege gnu.org aus dem Internet.

Billiges Imitat

Unter Debian- und Ubuntu-basierten Distributionen erhalten Sie beim Aufruf von parallel sehr wahrscheinlich eine kryptische Fehlermeldung (Abbildung 2). Um einen Konflikt mit Parallel aus dem moreutils-Paket zu vermeiden, kann sich GNU Parallel wie sein Konkurrent verhalten. Diesen Kompatibilitätsmodus haben die Debian-Paketbauer standardmäßig angeknipst. Um GNU Parallel wie hier beschrieben zu nutzen, müssen Sie entweder jedem Aufruf von parallel den Parameter --gnu mit auf den Weg geben oder aber in der Datei /etc/parallel/config den Eintrag --tollef entfernen.

Abbildung 2: Wenn diese Fehlermeldung erscheint, läuft GNU Parallel im falschen Modus.

Abbildung 2: Wenn diese Fehlermeldung erscheint, läuft GNU Parallel im falschen Modus.

Rückhaltebecken

Alle Ausgaben eines Programms sammelt GNU Parallel und gibt sie erst dann auf einmal aus, wenn das Programm beendet ist. Das hat den Vorteil, dass im obigen Beispiel die zwei gestarteten Ping-Instanzen nicht “durcheinander reden”, birgt aber auch den Nachteil, dass Sie keine Zwischenergebnisse sehen. Wünschen Sie dennoch eine sofortige Ausgabe, müssen Sie GNU Parallel noch den Parameter -u auf den Weg geben (Abbildung 3).

Der Parameter -k sorgt schließlich dafür, dass GNU Parallel die Ausgaben der Programme in exakt der Reihenfolge präsentiert, in der es sie aufgerufen hat. Im obigen Beispiel würden also immer erst die Ausgaben von ping -c2 gnu.org auf dem Schirm erscheinen, auch wenn ein anderes ping schneller mit der Arbeit fertig war. Um die Übersicht zu behalten, zwingen Sie GNU Parallel mit dem Parameter -v dazu, vor jede Ausgabe den Namen des zugehörigen Befehls zu schreiben.

Abbildung 3: Mit dem Parameter <code srcset=

-u erhalten Sie umgehend von den gestarteten Jobs eine Rückmeldung, was allerdings zu durchmischten Ausgaben führen kann (hier besonders am Anfang).” width=”300″ height=”214″ /> Abbildung 3: Mit dem Parameter -u erhalten Sie umgehend von den gestarteten Jobs eine Rückmeldung, was allerdings zu durchmischten Ausgaben führen kann (hier besonders am Anfang).

Job-Center

Funktioniert GNU Parallel wie erwartet, kann auch bei der Bildverarbeitung die Post abgehen. Dafür sorgt das Kommando

$ parallel mogrify -resize 50% ::: *.tif

Es verkürzte in unserem Test die Verarbeitungsdauer um die Hälfte. Damit Sie hinter den drei Doppelpunkten nicht mühsam alle zu bearbeitenden Dateien aufführen müssen, greift das Kommando auf die Hilfe der Shell zurück, die automatisch vorab *.tif in eine Liste mit allen TIFF-Dateien aus dem aktuellen Verzeichnis umwandelt. Für jedes dieser TIFFs ruft GNU Parallel dann das Programm mogrify -resize 50% auf.

GNU Parallel versucht dabei immer möglichst viele Jobs gleichzeitig zu starten – standardmäßig pro Prozessorkern einen. Auf einem Vierkern-Prozessor verkleinert das obige Kommando folglich immer vier Bilder gleichzeitig. Über den Parameter -j geben Sie die Anzahl der parallel gestarteten Jobs selbst vor. Nur zwei Bilder gleichzeitig wandelt beispielsweise:

$ parallel -j 2 mogrify -resize 50% ::: *.tif

Die Zahl hinter -j können Sie auch in einer Datei übergeben, in der dann die Anzahl der Jobs steht, wobei dahinter kein Zeilenumbruch folgen darf:

$ parallel -j /tmp/jobs.txt ...

GNU Parallel liest diese Textdatei nach jedem gestarteten Job neu ein. Das hat den Vorteil, dass Sie bei umfangreichen Bildersammlungen mitten in der Verarbeitung die Anzahl der Jobs ändern können. Wie viele und welche Jobs gerade laufen, verrät GNU Parallel auf der Standardfehlerausgabe (STDERR), sobald Sie ihm das SIGUSR1-Signal senden (Abbildung 4).

Abbildung 4: Auf das <code srcset=

SIGUSR1-Signal hin liefert GNU Parallel die Befehlszeilen der in diesem Moment laufenden Jobs. Hier sind das zwei Mogrify-Befehle.” width=”300″ height=”128″ /> Abbildung 4: Auf das SIGUSR1-Signal hin liefert GNU Parallel die Befehlszeilen der in diesem Moment laufenden Jobs. Hier sind das zwei Mogrify-Befehle.

Bei größeren Bildersammlungen verrät der GNU Parallel-Parameter --progress, wie weit die Verarbeitung bereits fortgeschritten ist. Mit --eta berechnet GNU Parallel auch noch die voraussichtliche Bearbeitungsdauer. Um die Verarbeitung abzubrechen, beenden Sie GNU Parallel einfach mit [Strg]+[C] oder senden ihm das TERM-Signal (etwa mit killall -TERM parallel). Im letzten Fall wartet GNU Parallel noch, bis die gerade laufenden Jobs ihre Arbeit beendet haben.

Übereignung

Die bisherigen Beispiele führten die Parameter für die Jobs hinter drei Doppelpunkten auf. Alternativ übergeben Sie die Parameter auch über eine Pipe (Listing 1, Zeile 1) oder sammeln alle Parameter – in diesem Fall die Dateinamen – in einer Textdatei (Zeile 2).

Listing 1

$ find . -name '*.tif' | parallel mogrify -resize 50%
$ parallel -a dateinamen.txt mogrify -resize 50%
$ find . -name '*.tif' | parallel -0 mogrify -resize 50%

In beiden Fällen geht GNU Parallel davon aus, dass jeder Parameter mit einem Zeilenumbruch endet. Einen Dateinamen mit Leerzeichen zerhackt das Programm folglich nicht in mehrere einzelne Parameter, sondern übergibt ihn Mogrify korrekt als Ganzes. Sollte jedoch ein Scherzbold in einem Dateinamen auch einen Zeilenumbruch eingeschmuggelt haben, muss man den Parameter -0 einbauen (Zeile 3). In diesem Fall verwendet GNU Parallel das Nullzeichen (\0) als Trennzeichen zwischen den Parametern.

Einbauküche

In den bisherigen Befehlen ersetzt Mogrify das Originalbild mit der geschrumpften Variante. Besser wäre es, würde die bearbeitete Fassung stattdessen in dem dafür eingerichteten Verzeichnis kleiner/ landen:

$ mogrify -resize 50% bild.tif kleiner/bild.tif

Damit taucht der Dateiname allerdings an gleich zwei Stellen auf. GNU Parallel darf also nicht mehr nur einfach den Dateinamen an den Mogrify-Befehl anhängen, sondern muss ihn an ganz bestimmten Positionen einbauen. Genau für diesen Zweck existiert der Platzhalter {} (Listing 2, Zeile 1).

Dabei setzt GNU Parallel den Parameter (in diesem Fall den Dateinamen) an die Stelle der Klammern {}. Diesen Platzhalter gibt es noch in weiteren praktischen Varianten. So schneidet etwa {.} die Dateierweiterung ab. Das können Sie wiederum nutzen, um flugs mit Convert die verkleinerten Bilder auch noch ins PNG-Format zu überführen (Zeile 2). Analog liefert der Platzhalter {//} den Verzeichnisnamen.

Listing 2

$ parallel mogrify -write kleiner/{} -resize 50% {} ::: *.tif
$ parallel convert {} {.}.png ::: *.tif

Nennen Sie GNU Parallel keinen Befehl, nimmt das Werkzeug an, dass die Eingabe die auszuführenden Befehle enthält. Auf diese Weise füttern Sie GNU Parallel mit mehreren verschiedenen Befehlen:

$ (echo ls; echo pwd) | parallel

Hier erzeugt zunächst die Shell mithilfe von echo die zwei Zeilen ls und pwd, welche sie dann an GNU Parallel übergibt, das die beiden Befehle dann wiederum gleichzeitig ausführt (Abbildung 5).

Abbildung 5: GNU Parallel interpretiert hier die Eingaben als Befehle.

Abbildung 5: GNU Parallel interpretiert hier die Eingaben als Befehle.

Dicke Dinger

Zeit frisst nicht nur das Umwandeln von Bildersammlungen, sondern auch das Verarbeiten großer Dateien. Auch hier hilft wieder GNU Parallel. So verfüttert der folgende Befehl beispielsweise die große Datei ubuntu.img an das Tool:

cat ubuntu.img | parallel --pipe --recend '' -k gzip >ubuntu.img.gz

Aufgrund des Parameters --pipe zerschneidet GNU Parallel die hereinfließenden Daten in 1 MByte große Blöcke. Jeden dieser Blöcke komprimiert es dann – wieder möglichst gleichzeitig – mittels Gzip. Die gepackten Datenblöcke sammelt das Tool wieder ein, bringt sie in die richtige Reihenfolge (-k) und legt sie dann in der Datei ubuntu.img.gz ab. Dieses Archiv lässt sich mit gzip -d ganz normal wieder entpacken.

Der Parameter --recend steht für “Record End”. Über ihn kann man angeben, wo jeweils ein Datenblock endet. Ohne andere Angabe nimmt GNU Parallel an, dass es an jedem entdeckten Zeilenumbruch ein weiteres Stück von der großen Datei abschneiden muss. Im obigen Beispiel soll es aber Blöcke von jeweils 1 MByte Größe an Gzip übergeben. Daher setzen die zwei einfachen Anführungsstriche '' die Voreinstellung von --recend außer Gefecht.

Nach dem gleichen Prinzip lassen sich auch andere große Dateien parallel verarbeiten. Die Dokumentation zu Parallel sortiert damit beispielsweise eine große Textdatei [4].

Das Einpacken mit Gzip hat übrigens einen kleinen Schönheitsfehler: Da GNU Parallel immer nur Häppchen packen lässt, sieht der Archivierer nicht die komplette Datei und kann sie daher nicht ganz so effizient verpacken. Wie groß die Abweichungen ausfallen, hängt von den zu komprimierenden Daten ab.

Ins Netz gegangen

Falls Ihnen die parallele Bearbeitung immer noch nicht schnell genug abläuft, kann GNU Parallel auch noch weitere Computer im Netzwerk einspannen. Dann verkleinert jeweils ein anderer Rechenknecht jedes Urlaubsfoto. Damit das funktioniert, müssen Sie sich via SSH auf den entfernten Computer ohne die Eingabe eines Passwortes anmelden können. Dabei hilft beispielsweise der ssh-agent [5].

Des Weiteren muss lokal noch Rsync und auf den entfernten Computern jeweils GNU Parallel installiert sein. Letzteres ist notwendig, damit GNU Parallel die Anzahl der Prozessoren ermitteln kann, Rsync dient zum Übertragen von Daten. Der folgende Befehl prüft, ob GNU Parallel auf die Rechner mit den IP-Adressen 192.168.2.11 und 192.168.2.12 zugreifen kann:

$ parallel --sshlogin 192.168.2.11,192.168.2.12 'hostname; echo' ::: 1 2

GNU Parallel meldet sich damit per SSH auf den beiden Rechnern an, startet dort jeweils das Programm Hostname und liefert dessen Ausgaben zurück (Abbildung 6). Das echo dient dabei als kleine Hilfe: GNU Parallel startet den Befehl ja für jeden hinter ::: aufgelisteten Parameter – doch hostname verlangt gar keine Parameter.

Abhilfe schafft das angehängte, harmlose echo, das der obige Befehl einfach eine Zahl ausgeben lässt. Die zwei Zahlen schließlich sind noch notwendig, damit GNU Parallel auch beide Rechner kontaktiert. Stünde dort lediglich die 1, würde das Tool den Befehl hostname; echo nur auf einem der Computer ausführen.

Abbildung 6: Das <code srcset=

echo und die Zahlen helfen hier dabei, jeden der angegebenen Server anzusprechen.” width=”300″ height=”74″ /> Abbildung 6: Das echo und die Zahlen helfen hier dabei, jeden der angegebenen Server anzusprechen.

Anstelle der IP-Adressen können Sie auch die Hostnamen verwenden, weitere Rechner hängen Sie durch Kommas getrennt hintereinander. Abweichende Login-Namen stellen Sie wie von SSH gewohnt mit einem Klammeraffen (@) vor den Rechnernamen. Hat die Kontaktaufnahme geklappt, können Sie die Bilder von der Computerbande umwandeln lassen.

Das dazu notwendige, etwas längliche Kommando zeigt Listing 3. Hier greift sich GNU Parallel die nächste Bilddatei und kopiert sie (--transfer) auf einen der Computer. Dort setzt es dann das Kommando mogrify -resize 50% auf die Datei an. Anschließend holt GNU Parallel die verarbeitete Datei wieder zurück (--return {}) und löscht die Kopie auf dem entfernten Rechner (--cleanup).

Listing 3

parallel --sshlogin hans@192.168.2.11,peter@192.168.2.12 --transfer --return {} --cleanup mogrify -resize 50% {} ::: *.tif

Um den gesamten Befehl übersichtlich zu halten, überschreiben mogrify und GNU Parallel hier wieder einfach die Originaldateien. Da die drei Parameter --transfer, --return und --cleanup in der Praxis extrem häufig zum Einsatz kommen, dürfen Sie sie mit --trc abkürzen.

Bei solchen verteilten Berechnungen im Netzwerk stellen die Netzwerkverbindungen den limitierenden Flaschenhals dar. So dauert der Transfer der Bilder von und zu den Rechenknechten unter Umständen länger als das Bearbeiten auf dem lokalen PC. Die verteilte Berechnung lohnt daher meist nur, wenn diese Berechnungen umfangreich sind beziehungsweise besonders lange dauern.

Fazit

GNU Parallel spielt seine Stärken immer dann aus, wenn sich eine rechenintensive Aufgabe in mehrere, voneinander unabhängige Teilaufgaben zerlegen lässt. In Bash-Skripten können Sie häufig For- und While-Schleifen mit GNU Parallel beschleunigen, wie etwa im Beispiel mit den Bildern ganz zu Beginn des Artikels. Angenehmer Nebeneffekt: Die Kommandos lassen sich dank GNU Parallel auch noch wesentlich besser lesen.

Das kleine Werkzeug kennt noch unzählige weitere nützliche Parameter und Funktionen, deren Beschreibung ein ansehnliches Buch füllen würden. Wer zum ersten Mal mit Parallel arbeitet, sollte sich daher erst die Beispiele im Handbuch ansehen [6].

Übrigens haben wir in den Beispielen etwas gemogelt: Mogrify muss nicht, wie zu Beginn des Artikels, in einer For-Schleife stecken, sondern bearbeitet selbst mehrere Dateien. Es nutzt dabei sogar automatisch mehrere Prozessorkerne. GNU Parallel ist folglich auch keine Allzweckwaffe. Sie sollten vor seinem Einsatz immer prüfen, ob die von Ihnen aufgerufenen Kommandozeilenbefehle nicht schon von sich aus mehrere Kerne nutzen. 

Infos

[1] GNU Parallel: http://www.gnu.org/software/parallel/

[2] Moreutils: http://joeyh.name/code/moreutils/

[3] GNU Parallel herunterladen: http://ftp.gnu.org/gnu/parallel/

[4] Sortieren der Zeilen einer Textdatei: http://www.gnu.org/software/parallel/man.html#example__processing_a_big_file_using_more_cores

[5] SSH, SCP und SFTP: Heike Jurzik, “Aber sicher!”, LU 03/2008, S. 84, https://www.linux-community.de/14752

[6] Handbuch zu GNU Parallel: http://www.gnu.org/software/parallel/man.html

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDF
LinuxUser 06/2013 KAUFEN
EINZELNE AUSGABE
ABONNEMENTS
TABLET & SMARTPHONE APPS
E-Mail Benachrichtigung
Benachrichtige mich zu:

Hinweis: Dieser Artikel ist älter als ein Jahr, enthaltene Informationen sind möglicherweise veraltet.

1 Kommentar
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Tim Schürmann
13 Jahre her

Im Satz:

“Dafür benötigen Sie lediglich Make, einen C-Compiler sowie das aktuelle Quellcode-Archiv [3].”

hat sich ein klitzekleiner Fehler eingeschlichen: GNU Parallel ist in Perl geschrieben, folglich benötigt man nicht den C-Compiler, sondern Perl. Die systemweite Installation funktioniert aber wie beschrieben per “configure && make && make install”.

Wer es noch schneller möchte, kann eine “10-Sekunden-Installation” anstoßen:

(wget -O – pi.dk/3 | sh)

Man kann parallel aber auch gänzlich ohne Installation nutzen, das Skript “parallel” liegt im Unterverzeichnis “src” des Quellcodearchivs.

Dank an Ole Tange für die Hinweise.

Nach oben