Leistung von Loop gegen Expansion

9

Benötigen Sie Expertenvorschläge zum folgenden Vergleich:

Codesegment mit Schleife:

for file in `cat large_file_list`
do
    gzip -d $file
done

Codesegment mit einfacher Erweiterung:

gzip -d `cat large_file_list`

Welches wird schneller sein? Müssen große Datenmengen manipulieren.

Leon
quelle
1
Die richtige Antwort hängt davon ab, wie lange der Start gzipauf Ihrem System dauert, wie viele Dateien in der Dateiliste enthalten sind und wie groß diese Dateien sind.
Kusalananda
Die Dateiliste enthält ca. 1000 - 10000 Dateien. Die Größe variiert zwischen einigen Kilobyte und 500 MB. Ich habe keine Ahnung, wie lange es dauert, gzip in meinem System zu starten . irgendwie überprüfen?
Leon
1
Ok, dann kann es auch von der Länge der Dateinamen abhängen . Wenn die Dateinamen lang sind, generieren einige Systeme möglicherweise den Fehler "Argumentliste zu lang", wenn Sie versuchen, dies ohne Schleife zu tun, da die Befehlsersetzung zu einer zu langen Befehlszeile für die Ausführung der Shell führen würde. Wenn Sie nicht von der Anzahl der Dateien in der Liste abhängig sein möchten, verwenden Sie einfach eine Schleife. Verbringen Sie viel Zeit damit, diese Dateien zu dekomprimieren, verglichen mit der anderen Verarbeitung, die Sie für sie ausführen werden?
Kusalananda
Leon schaut sich meine Testergebnisse an: "Riesen-Arglist" ist in meiner Einstellung 20x schneller als "Loop".
Verwenden Sie für ein glückliches Medium zwischen Prozessstart und Befehlszeilenlänge etwas wie xargs gzip -d < large_file_list, tr \\n \\0 large_file_list | xargs -0 gzip -d
achten Sie

Antworten:

19

Komplikationen

Folgendes funktioniert nur manchmal:

gzip -d `cat large_file_list`

Drei Probleme sind (in bashund den meisten anderen Bourne-ähnlichen Muscheln):

  1. Es schlägt fehl, wenn ein Dateiname Leerzeichen oder Zeilenumbrüche enthält (vorausgesetzt, er $IFSwurde nicht geändert). Dies liegt an der Wortteilung der Shell .

  2. Es kann auch fehlschlagen, wenn ein Dateiname globaktive Zeichen enthält. Dies liegt daran, dass die Shell die Pfadnamenerweiterung auf die Dateiliste anwendet .

  3. Es schlägt auch fehl, wenn Dateinamen mit beginnen -(wenn dies POSIXLY_CORRECT=1nur für die erste Datei gilt) oder wenn ein Dateiname vorhanden ist -.

  4. Es schlägt auch fehl, wenn zu viele Dateinamen darin sind, um in eine Befehlszeile zu passen.

Der folgende Code unterliegt denselben Problemen wie der obige Code (mit Ausnahme des vierten).

for file in `cat large_file_list`
do
    gzip -d $file
done

Zuverlässige Lösung

Wenn Sie large_file_listgenau einen Dateinamen pro Zeile haben und eine aufgerufene Datei -nicht dazu gehört und Sie sich auf einem GNU-System befinden, verwenden Sie:

xargs -rd'\n' gzip -d -- <large_file_list

-d'\n'weist xargsan, jede Eingabezeile als separaten Dateinamen zu behandeln.

-rweist xargsan, den Befehl nicht auszuführen, wenn die Eingabedatei leer ist.

--weist darauf hin, gzipdass die folgenden Argumente nicht als Optionen behandelt werden sollen, selbst wenn sie mit beginnen -. -allein würde trotzdem als -anstelle der aufgerufenen Datei behandelt -.

xargsEs werden viele Dateinamen in jede Befehlszeile eingefügt, jedoch nicht so viele, dass das Befehlszeilenlimit überschritten wird. Dies reduziert die Häufigkeit, mit der ein gzipProzess gestartet werden muss, und macht dies daher schnell. Es ist auch sicher: Die Dateinamen werden auch vor Wortteilung und Pfadnamenerweiterung geschützt .

John1024
quelle
Vielen Dank für die ausführliche Antwort. Ich verstehe Ihre 3 genannten Probleme. Der Dateiname ist einfach und stellt sich diesen Herausforderungen nicht, da die Liste bis zu 20000 enthält. Meine Frage bezieht sich im Wesentlichen auf die Leistung dieser beiden Segmente. Vielen Dank.
Leon
1
@Leon Die forSchleife wird bei weitem die langsamste sein. Die beiden anderen Methoden sind sehr nahe beieinander.
John1024
7
Auch nicht entlassen , die möglichen Probleme: viele , viele Fragen hier auf Stack sind , weil einzelne Wörter aufgespalten oder Pfadnamenerweiterung , um Menschen passiert , die es nicht erwartet hatten.
John1024
5
Beachten Sie auch, dass es beim Lesen einer Datei Abweichungen gibt mit xargs: Zumindest die GNU-Version verfügt über eine --arg-fileOption (Kurzform -a). Also könnte man es xargs -a large_file_list -rd'\n' gzip -d stattdessen tun . Tatsächlich gibt es keinen Unterschied, abgesehen von der Tatsache, dass <es sich um einen Shell-Operator handelt, der xargsvon stdin lesen würde (welche Shell "verlinkt"), während -adie xargs
betreffende
2
terdon bemerkte in einem anderen Kommentar über die Verwendung parallelzum Ausführen mehrerer Kopien von gzip, aber xargs(zumindest die GNU- Kopie ) hat auch dafür den -PSchalter. Auf Multicore-Computern kann dies einen Unterschied machen. Es ist aber auch möglich, dass die Dekomprimierung ohnehin vollständig E / A-gebunden ist.
Ilkkachu
12

Ich bezweifle, dass es viel ausmachen würde.

Ich würde eine Schleife verwenden, nur weil ich nicht weiß, wie viele Dateien in der Listendatei aufgeführt sind, und ich (im Allgemeinen) nicht weiß, ob einer der Dateinamen Leerzeichen in seinen Namen hat. Eine Befehlsersetzung, die eine sehr lange Liste von Argumenten erzeugen würde, kann zu einem Fehler "Argumentliste zu lang" führen, wenn die Länge der generierten Liste zu lang ist.

Meine Schleife würde so aussehen

while IFS= read -r name; do
    gunzip "$name"
done <file.list

Dies würde mir zusätzlich erlauben, Befehle zum Verarbeiten der Daten nach dem gunzipBefehl einzufügen . Abhängig davon, was die Daten tatsächlich sind und was damit zu tun ist, kann es sogar möglich sein, sie zu verarbeiten, ohne sie in einer Datei zu speichern:

while IFS= read -r name; do
    zcat "$name" | process_data
done <file.list

(Wo process_dataist eine Pipeline, die die unkomprimierten Daten von der Standardeingabe liest?)

Wenn die Verarbeitung der Daten länger dauert als die Dekomprimierung, ist die Frage, ob eine Schleife effizienter ist oder nicht, irrelevant.

Im Idealfall würde ich es jedoch vorziehen, eine Liste von Dateinamen nicht zu bearbeiten und stattdessen ein Dateinamen-Globbing-Muster wie in zu verwenden

for name in ./*.gz; do
    # processing of "$name" here
done

Wo ./*.gzist ein Muster, das mit den relevanten Dateien übereinstimmt. Auf diese Weise sind wir weder von der Anzahl der Dateien noch von den in den Dateinamen verwendeten Zeichen abhängig (sie können Zeilenumbrüche oder andere Leerzeichen enthalten oder mit Bindestrichen usw. beginnen).

Verbunden:

Kusalananda
quelle
5

Von diesen beiden ist die mit allen Dateien, die an einen einzelnen Aufruf von übergeben wurden, gzipwahrscheinlich schneller, genau weil Sie nur gzipeinmal starten müssen . (Das heißt, wenn der Befehl überhaupt funktioniert, lesen Sie die anderen Antworten für die Vorbehalte.)

Aber ich möchte an die goldene Regel der Optimierung erinnern : Tun Sie es nicht vorzeitig.

  1. Optimieren Sie so etwas nicht, bevor Sie wissen, dass es ein Problem ist.

    Dauert dieser Teil des Programms lange? Das Dekomprimieren großer Dateien könnte dazu führen, dass Sie es trotzdem tun müssen, sodass die Beantwortung möglicherweise nicht so einfach ist.

  2. Messen. Wirklich, es ist der beste Weg, um sicher zu sein.

    Sie sehen die Ergebnisse mit Ihren eigenen Augen (oder mit Ihrer eigenen Stoppuhr) und sie gelten für Ihre Situation, die zufällige Antworten im Internet möglicherweise nicht bieten. Fügen Sie beide Varianten in Skripte ein und führen Sie time script1.shund aus time script2.sh. (Führen Sie dies mit einer Liste leerer komprimierter Dateien durch, um den absoluten Overhead zu messen.)

ilkkachu
quelle
0

Wie schnell ist deine Festplatte?

Dies sollte alle Ihre CPUs verwenden:

parallel -X gzip -d :::: large_file_list

Ihr Limit wird also wahrscheinlich die Geschwindigkeit Ihrer Festplatte sein.

Sie können versuchen, sich anzupassen mit -j:

parallel -j50% -X gzip -d :::: large_file_list

Dadurch wird die Hälfte der Jobs wie beim vorherigen Befehl parallel ausgeführt und Ihre Festplatte wird weniger belastet. Abhängig von Ihrer Festplatte kann dies schneller sein.

Ole Tange
quelle