Kontext
Ich habe ein Verzeichnis mit Tausenden von Zip-Dateien, die im Formular datiert sind YYYYMMDD_hhmmss.zip
und jeweils etwa 300 KB groß sind. In jeder Zip-Datei befinden sich ca. 400 XML-Dateien mit jeweils ca. 3 KB.
Das Problem
Ich muss in der Lage sein, eine bestimmte Zeichenfolge innerhalb eines Datumsbereichs der Zip-Dateien zu suchen und zu finden.
Die aktuelle (wenn auch mittelmäßige) Lösung
Ich habe den folgenden Einzeiler
find /home/mydir/ -type f | sort | \
awk "/xml_20140207_000016.zip/,/xml_20140207_235938.zip/" | \
xargs -n 1 -P 10 zipgrep "my search string"
Der Punkt ist zu
- Listen Sie alle Dateien in meinem Verzeichnis mit tausend Dateien auf
- Sortieren Sie diese Liste von Dateien
- Abrufen einer Reihe von Dateien basierend auf bestimmten Daten (dieser
awk
Befehl druckt nur Zeilen nach dieser ersten übereinstimmenden Zeichenfolge und bis zu dieser zweiten übereinstimmenden Zeichenfolge). - Übergeben Sie jede Zeile des Ergebnisses, die einer einzelnen Datei entspricht, an
zipgrep
Die Frage
Dieser Einzeiler läuft schrecklich langsam, selbst mit 10 Prozessen auf einer 24-Kern-Maschine. Ich glaube, es ist langsam wegen des zipgrep
Befehls, aber ich bin nicht klug genug zu wissen, wie man es verbessert. Ich weiß nicht, ob ich es sein sollte, aber es ist mir ein wenig peinlich, dass ein Kollege ein Java-Tool geschrieben hat, das schneller läuft als dieses Skript. Ich würde das gerne umkehren, wenn es möglich ist. Weiß dann jemand, wie man diesen Befehl in diesem Zusammenhang schneller macht? Oder um irgendeinen Teil davon überhaupt zu verbessern?
unzip -p
oderunzip -c
eine kleine Verbesserung bewirken .unzip -c
ob die Zip-Datei überhaupt für Ihre Ergebnisse relevant ist, und erst dann die einzelnen darin enthaltenen Dateien genauer untersuchen.Antworten:
Es gibt einen Teil, den Sie leicht verbessern können, aber es ist nicht der langsamste Teil.
Dies ist etwas verschwenderisch, da zuerst alle Dateien aufgelistet, dann die Dateinamen sortiert und die interessanten extrahiert werden. Der
find
Befehl muss vollständig ausgeführt werden, bevor die Sortierung beginnen kann.Es wäre schneller, zunächst nur die interessanten Dateien aufzulisten oder zumindest eine möglichst kleine Obermenge. Wenn Sie einen feinkörnigeren Filter für Namen benötigen, als dies möglich
find
ist, leiten Sie in awk ein, aber sortieren Sie nicht: awk und andere zeilenweise Filter können Zeilen einzeln verarbeiten, aber die Sortierung erfordert die vollständige Eingabe.Der Teil, der am offensichtlichsten suboptimal ist, ist zipgrep. Hier gibt es aufgrund der Einschränkungen der Shell-Programmierung keine einfache Möglichkeit, die Leistung zu verbessern. Das zipgrep-Skript listet die Dateinamen im Archiv auf und ruft
grep
nacheinander den Inhalt jeder Datei auf. Dies bedeutet, dass das Zip-Archiv für jede Datei immer wieder analysiert wird. Ein Java-Programm (oder Perl oder Python oder Ruby usw.) kann dies vermeiden, indem die Datei nur einmal verarbeitet wird.Wenn Sie sich an die Shell-Programmierung halten möchten, können Sie versuchen, jede Zip-Datei zu mounten, anstatt zipgrep zu verwenden.
Beachten Sie, dass Parallelität Ihnen nicht viel hilft: Der begrenzende Faktor bei den meisten Setups ist die Festplatten-E / A-Bandbreite und nicht die CPU-Zeit.
Ich habe noch kein Benchmarking durchgeführt, aber ich denke, der größte Verbesserungspotenzial wäre die Verwendung einer Zipgrep-Implementierung in einer leistungsfähigeren Sprache.
quelle
Einige schnelle Ideen;
find
sort
Bit auch nicht benötigenWenn diese beiden Teile nicht im Weg sind und der Datumsbereich bekannt ist, können Sie anstelle von awk einen einfachen Dateinamen-Glob verwenden. Zum Beispiel (vorausgesetzt, Ihre Shell ist
bash
):Alle Dateien eines Tages
echo xml_20140207_*.zip | xargs -n 1 -P 10 zipgrep "my search string"
Dateien, die zwischen 15:00 und 18:00 Uhr erstellt wurden, entweder am 07. Februar oder am 10. Februar 2014:
echo xml_201402{07,10}_1{5..7}*.zip | xargs -n 1 -P 10 zipgrep "my search string"
quelle
Es ist nicht klar, wo Ihr Engpass liegt. Nehmen wir an, es ist beim Lesen der Dateien. Abhängig von Ihrem Speichersystem ist es schneller, die gesamte Datei vor der Verarbeitung zu lesen. Dies gilt insbesondere für
zipgrep
einige Suchvorgänge in der Datei: Wenn sich die Datei nicht vollständig im Speicher befindet, warten Sie auf die Suche auf der Festplatte.Das obige wird jeweils
cat
eine Datei und damit in den Speichercache legen, dann einezipgrep
pro CPU ausführen , die dann aus dem Speichercache liest.Ich habe RAID-Systeme verwendet, bei denen Sie eine 6-fache Geschwindigkeit erzielt haben, indem Sie 10 Dateien parallel gelesen haben, anstatt jeweils 1 Datei oder 30 Dateien parallel zu lesen. Wenn ich die oben auf diesem RAID - System laufen hätte, würde ich einstellen
-j1
zu-j10
.Wenn Sie stattdessen GNU Parallel verwenden
xargs
, schützen Sie sich vor dem Mischen der Ausgabe (siehe http://www.gnu.org/software/parallel/man.html#DIFFERENCES-BETWEEN-xargs-AND-GNU-Parallel ).quelle