Wie man GNU parallel effektiv nutzt

8

Angenommen, ich möchte alle Übereinstimmungen in einer komprimierten Textdatei finden:

$ gzcat file.txt.gz | pv --rate -i 5 | grep some-pattern

pv --ratewird hier zur Messung des Rohrdurchsatzes verwendet. Auf meinem Computer sind es ungefähr 420 MBit / s (nach der Dekomprimierung).

Jetzt versuche ich paralleles grep mit GNU parallel zu machen.

$ gzcat documents.json.gz | pv --rate -i 5 | parallel --pipe -j4 --round-robin grep some-pattern

Jetzt sinkt der Durchsatz auf ~ 260 MBit / s. Und was noch interessanter parallelist, ist, dass viel CPU verbraucht wird. Mehr als grepProzesse (aber weniger als gzcat).

EDIT 1 : Ich habe verschiedene Blockgrößen ( --block) sowie verschiedene Werte für -N/ -Loptions ausprobiert . An dieser Stelle hilft mir nichts.

Was mache ich falsch?

Denis Bazhenov
quelle

Antworten:

9

Ich bin wirklich überrascht, dass Sie mit GNU Parallel 270 MB / s erreichen --pipe. Meine Tests erreichen normalerweise eine maximale Geschwindigkeit von 100 MB / s.

Ihr Engpass ist höchstwahrscheinlich in GNU Parallel: --pipeist nicht sehr effizient. --pipepartist jedoch: Hier kann ich in der Größenordnung von 1 GB / s pro CPU-Kern bekommen.

Leider gibt es einige Einschränkungen hinsichtlich der Verwendung --pipepart:

  • Die Datei muss durchsuchbar sein (dh keine Pipe)
  • Sie müssen in der Lage sein, den Anfang eines Datensatzes mit --recstart / - recend zu finden (dh keine komprimierte Datei).
  • Die Zeilennummer ist unbekannt (Sie können also keinen 4-Zeilen-Datensatz haben).

Beispiel:

parallel --pipepart -a bigfile --block 100M grep somepattern
Ole Tange
quelle
1
Vielen Dank. Gibt es einen Grund, warum --pipees ineffizient ist? Ich meine, ist es ein grundlegendes Problem oder eher ein spezifisches Implementierungsproblem?
Denis Bazhenov
2
Ja: GNU Parallel ist in Perl geschrieben und muss mit --pipejedem einzelnen Byte den einzelnen Prozess durchlaufen, der für jedes Byte ein wenig verarbeitet werden muss. Die --pipepartmeisten Bytes werden vom zentralen Prozess nie gesehen: Sie werden von gespawnten Jobs verarbeitet. Da es nur wenige Zeilen gibt, die den Engpass darstellen, --pipewürde ich einen C / C ++ - Codierer begrüßen, der den Teil neu schreibt, der dann für Leute ausgeführt wird, die C-Compiler im Pfad haben.
Ole Tange
2

grep ist sehr effektiv - es macht keinen Sinn, es parallel auszuführen. In Ihrem Befehl benötigt nur die Dekomprimierung mehr CPU, dies kann jedoch nicht parallelisiert werden.

Das Teilen von Eingaben durch Parallele benötigt mehr CPU als das Teilen von übereinstimmenden Zeilen durch Grep.

Situationsänderung, wenn Sie anstelle von grep etwas verwenden möchten, das für jede Zeile viel mehr CPU benötigt - dann hätte Parallel mehr Sinn.

Wenn Sie diesen Vorgang beschleunigen möchten - schauen Sie, wo Engpässe liegen - wahrscheinlich ist es Dekomprimierung (hilft dann bei der Verwendung eines anderen Dekomprimierungswerkzeugs oder einer besseren CPU) oder - Lesen von der Festplatte (dann Hilfe bei der Verwendung eines anderen Dekomprimierungswerkzeugs oder eines besseren Festplattensystems).

Nach meiner Erfahrung - manchmal ist es besser, lzma (zum Beispiel -2) zum Komprimieren / Dekomprimieren von Dateien zu verwenden - hat es eine höhere Komprimierung als gzip, sodass viel weniger Daten von der Festplatte gelesen werden müssen und die Geschwindigkeit vergleichbar ist.

undefinieren
quelle
1
In der Tat ist es mein Fall. Anstelle von grep wird ein sehr CPU-hungriger Java-Prozess verwendet. Ich habe die Frage ein wenig vereinfacht. Und dennoch bedeutet das parallele Essen einer Menge CPU nicht viel Arbeit für Java-Prozesse.
Denis Bazhenov
1

Die Dekompression ist hier der Engpass. Wenn die Dekomprimierung nicht intern parallelisiert wird, erreichen Sie sie nicht selbst. Wenn Sie mehr als einen solchen Job haben, starten Sie sie natürlich parallel, aber Ihre Pipeline allein ist schwer zu parallelisieren. Das Aufteilen eines Streams in parallele Streams lohnt sich fast nie und kann beim Synchronisieren und Zusammenführen sehr schmerzhaft sein. Manchmal muss man einfach akzeptieren, dass mehrere Kerne nicht bei jeder einzelnen Aufgabe helfen, die Sie ausführen.

Im Allgemeinen sollte die Parallelisierung in der Shell hauptsächlich auf der Ebene unabhängiger Prozesse erfolgen.

Orion
quelle
1
Es scheint nicht, dass Dekomprimierung ein Engpass bei der Verwendung ist parallel. Ich stimme zu, dass dies sicherlich im ersten Fall (ohne Parallele) der Fall ist, aber im zweiten Fall (mit Parallelität) liegt der Engpass auf der parallelen Seite. Dies folgt aus der Beobachtung, dass der Durchsatz signifikant abfällt, gemessen durch pv. Wenn der Engpass dekomprimiert wird, ändert der Durchsatz nichts, was Sie der Pipeline hinzufügen. Ich denke, es ist eine sehr intuitive Definition des Durchsatzes - die Sache, die den Durchsatz am meisten einschränkt.
Denis Bazhenov
1
Es ist möglich, dass grep so schnell ist, dass es schneller beendet wird, als paralleles in seine Pipe schreiben kann. In diesem Fall grepwarten die meisten Prozesse einfach darauf, mehr zu erhalten, während parallelsie rund um die Uhr arbeiten, um die Blöcke in mehrere Pipes zu multiplexen (dies sind zusätzliche E / A-Operationen und können sogar die Dekomprimierung blockieren, wenn der Puffer voll ist). Haben Sie auch versucht, mit dem --blockParameter zu spielen ? Der Standardwert ist 1Mso lange, bis ein Grep 1MDaten erhält . Der Rest ist mit ziemlicher Sicherheit bereits erledigt. Daher kommen wir auf die Tatsache zurück, dass es keinen Sinn macht, dies zu parallelisieren.
Orion
1
Ja, ich habe diese Optionen mit großen und kleinen Blockgrößen ausprobiert. Sowie verschiedene Werte für -N/ -Loptions. Es scheint, als ob die Standardoptionen dem lokalen Optimum sehr nahe kommen, das ich erlebt habe :)
Denis Bazhenov
1
Versuchen Sie es mit und ohne pv(mit time) zu steuern . Auf diese Weise können Sie sehen, ob es sich pvselbst verlangsamt. Wenn dies der parallelFall ist, ist das Kopieren von Daten in Pipes definitiv ein zusätzlicher Aufwand. Und auf jeden Fall bin ich mir ziemlich sicher, dass grepdies in diesem Fall fast in Echtzeit geschieht, insbesondere wenn das Muster eine einfache Zeichenfolge ohne viel Backtracking ist. Zusätzlich parallelwerden die grepAusgänge verschachtelt und durcheinander gebracht .
Orion
1
Ich werde überprüfen, dass pvselbst das Problem nicht verursacht, danke für den Rat.
Denis Bazhenov