Ich arbeite normalerweise mit Textdateien mit einer Größe von ~ 20 GB und zähle sehr oft die Anzahl der Zeilen in einer bestimmten Datei.
So wie ich es jetzt mache, ist es einfach cat fname | wc -l
und es dauert sehr lange. Gibt es eine Lösung, die viel schneller wäre?
Ich arbeite in einem Hochleistungscluster mit installiertem Hadoop. Ich habe mich gefragt, ob ein Ansatz zur Kartenreduzierung helfen könnte.
Ich möchte, dass die Lösung so einfach wie ein Zeilenlauf ist, wie die wc -l
Lösung, aber nicht sicher, wie machbar sie ist.
Irgendwelche Ideen?
wc -l fname
kann schneller sein. Sie können auch versuchen,vim -R fname
ob dies schneller ist (es sollte Ihnen die Anzahl der Zeilen nach dem Start anzeigen).Antworten:
Versuchen:
sed -n '$=' filename
Auch Katze ist unnötig:
wc -l filename
reicht auf Ihre derzeitige Weise aus.quelle
sed
es schneller gegangen. Vielleicht kann ein kleines Benchmarking helfen, es besser zu verstehen.wc -l filename
zuerst ausgeführt haben, dann sind Sie ausgeführt wordensed -n '$=' filename
, sodass wc beim ersten Durchlauf die gesamte Datei von der Festplatte lesen musste, damit sie vollständig auf Ihrem wahrscheinlich größer als 3 GB großen Speicher zwischengespeichert werden kannsed
könnte gleich viel schneller laufen. Ich habe die Tests selbst mit einer 4-GB-Datei auf einem Computer mit 6-GB-RAM durchgeführt, aber ich habe sichergestellt, dass sich die Datei bereits im Cache befindet. die Punktzahl:sed
- 0m12.539s,wc -l
- 0m1.911s.wc
War also 6,56 mal schneller. Das Experiment wurde wiederholt, aber der Cache vor jedem Lauf geleert. Beide dauerten ungefähr 58 Sekunden.Ihr begrenzender Geschwindigkeitsfaktor ist die E / A-Geschwindigkeit Ihres Speichergeräts. Ein Wechsel zwischen einfachen Zeilenumbrüchen / Musterzählprogrammen hilft daher nicht weiter, da der Unterschied in der Ausführungsgeschwindigkeit zwischen diesen Programmen wahrscheinlich durch die Art und Weise unterdrückt wird, in der die Festplatte / der Speicher langsamer ist. was auch immer du hast.
Wenn Sie jedoch dieselbe Datei auf Festplatten / Geräte kopiert haben oder die Datei auf diese Festplatten verteilt ist, können Sie den Vorgang auf jeden Fall parallel ausführen. Ich weiß nicht genau über diesen Hadoop Bescheid, aber vorausgesetzt, Sie können die Datei mit 10 GB von 4 verschiedenen Speicherorten aus lesen, können Sie 4 verschiedene Zeilenzählprozesse ausführen, jeder in einem Teil der Datei, und ihre Ergebnisse zusammenfassen:
Beachten Sie das
&
an jeder Befehlszeile, damit alle parallel ausgeführt werden.dd
funktioniert wiecat
hier, aber lassen Sie uns angeben, wie viele Bytes gelesen werden sollen (count * bs
Bytes) und wie viele am Anfang der Eingabeskip * bs
übersprungen werden sollen ( Bytes). Es funktioniert in Blöcken, daher muss angegeben werdenbs
die Blockgröße angegeben werden. In diesem Beispiel habe ich die 10-GB-Datei in 4 gleiche Blöcke von 4 KB * 655360 = 2684354560 Byte = 2,5 GB partitioniert, eine für jeden Auftrag. Möglicherweise möchten Sie ein Skript einrichten, das dies basierend auf der Größe der Datei für Sie erledigt Datei und die Anzahl der parallelen Jobs, die Sie ausführen werden. Sie müssen auch das Ergebnis der Ausführungen zusammenfassen, was ich wegen meines Mangels an Shell-Skript-Fähigkeiten nicht getan habe.Wenn Ihr Dateisystem intelligent genug ist, um große Dateien auf viele Geräte wie ein RAID oder ein verteiltes Dateisystem oder ähnliches aufzuteilen und E / A-Anforderungen, die parallelisiert werden können, automatisch zu parallelisieren, können Sie eine solche Aufteilung durchführen, indem Sie viele parallele Jobs ausführen, aber verwenden der gleiche Dateipfad, und Sie können immer noch einen gewissen Geschwindigkeitsgewinn haben.
BEARBEITEN: Eine andere Idee, die mir gekommen ist, ist, wenn die Zeilen in der Datei dieselbe Größe haben, können Sie die genaue Anzahl der Zeilen erhalten, indem Sie die Größe der Datei durch die Größe der Zeile dividieren, beide in Bytes. Sie können dies fast augenblicklich in einem einzigen Job tun. Wenn Sie die mittlere Größe haben und sich nicht genau um die Zeilenanzahl kümmern, aber eine Schätzung wünschen, können Sie dieselbe Operation ausführen und ein zufriedenstellendes Ergebnis viel schneller als die exakte Operation erzielen.
quelle
Verwenden Sie auf einem Multi-Core-Server GNU parallel, um Dateizeilen parallel zu zählen. Nachdem die Zeilenanzahl jeder Datei gedruckt wurde, summiert bc alle Zeilenzahlen.
Um Platz zu sparen, können Sie sogar alle Dateien komprimieren. In der folgenden Zeile wird jede Datei dekomprimiert und ihre Zeilen parallel gezählt. Anschließend werden alle Zählungen summiert.
quelle
dd
stattwc
zum Lesen der Datei, wenn ein Festplattenengpass ein Problem darstellt.Gemäß meinem Test kann ich überprüfen, ob die Spark-Shell (basierend auf Scala) viel schneller ist als die anderen Tools (GREP, SED, AWK, PERL, WC). Hier ist das Ergebnis des Tests, den ich für eine Datei mit 23782409 Zeilen ausgeführt habe
real 0m44.96s Benutzer 0m41.59s sys 0m3.09s
real 0m37.57s Benutzer 0m33.48s sys 0m3.97s
real 0m38.22s Benutzer 0m28.05s sys 0m10.14s
time perl -ne 'END { $_=$.;if(!/^[0-9]+$/){$_=0;};print "$_" }' my_file.txt
;;real 0m23.38s Benutzer 0m20.19s sys 0m3.11s
real 0m19.90s Benutzer 0m16.76s sys 0m3.12s
res1: org.joda.time.Seconds = PT15S
quelle
time
, um die Laufzeit zu erhalten.Wenn sich Ihre Daten in HDFS befinden, ist der schnellste Ansatz möglicherweise die Verwendung von Hadoop-Streaming. Die COUNT UDF von Apache Pig arbeitet mit einem Beutel und verwendet daher einen einzelnen Reduzierer, um die Anzahl der Zeilen zu berechnen. Stattdessen können Sie die Anzahl der Reduzierungen in einem einfachen Hadoop-Streaming-Skript wie folgt manuell festlegen:
Beachten Sie, dass ich die Anzahl der Reduzierstücke manuell auf 100 eingestellt habe, Sie diesen Parameter jedoch einstellen können. Sobald der Map-Reduction-Job abgeschlossen ist, wird das Ergebnis jedes Reduzierers in einer separaten Datei gespeichert. Die endgültige Anzahl der Zeilen ist die Summe der von allen Reduzierern zurückgegebenen Zahlen. Sie können die endgültige Anzahl der Zeilen wie folgt erhalten:
quelle
Ich weiß, dass die Frage jetzt ein paar Jahre alt ist, aber dieses Bash-Skript erweitert Ivellas letzte Idee und schätzt die Zeilenanzahl einer großen Datei innerhalb von Sekunden oder weniger, indem es die Größe einer Zeile misst und daraus extrapoliert:
Wenn Sie dieses Skript
lines.sh
benennen, können Sie aufrufenlines.sh bigfile.txt
, um die geschätzte Anzahl der Zeilen abzurufen. In meinem Fall (ca. 6 GB, Export aus der Datenbank) betrug die Abweichung von der tatsächlichen Zeilenanzahl nur 3%, lief jedoch ca. 1000-mal schneller. Übrigens habe ich die zweite, nicht die erste Zeile als Basis verwendet, da die erste Zeile Spaltennamen hatte und die tatsächlichen Daten in der zweiten Zeile begannen.quelle
Hadoop bietet im Wesentlichen einen Mechanismus, um etwas Ähnliches auszuführen, wie es @Ivella vorschlägt.
Das HDFS (Distributed File System) von Hadoop nimmt Ihre 20-GB-Datei und speichert sie im gesamten Cluster in Blöcken fester Größe. Nehmen wir an, Sie konfigurieren die Blockgröße auf 128 MB. Die Datei wird in Blöcke von 20 x 8 x 128 MB aufgeteilt.
Sie würden dann ein Kartenreduzierungsprogramm über diese Daten ausführen, im Wesentlichen die Zeilen für jeden Block (in der Kartenphase) zählen und dann diese Blockzeilenzahlen zu einer endgültigen Zeilenanzahl für die gesamte Datei reduzieren.
Was die Leistung betrifft, ist im Allgemeinen die Leistung umso besser, je größer Ihr Cluster ist (mehr WC laufen parallel über unabhängigere Festplatten). Die Job-Orchestrierung ist jedoch mit einem gewissen Aufwand verbunden, der bedeutet, dass die Ausführung des Jobs auf kleineren Dateien nicht schneller erfolgt Durchsatz als ein lokales WC ausführen
quelle
Ich bin mir nicht sicher, ob Python schneller ist:
quelle
...read().split("\n")
. Ändern Sie das fürsum(1 for line in open("mybigfile.txt"))
und Sie haben einen besseren naiven Ansatz (ich nehme keinen Vorteil aus dem HDFS-Setup)Wenn Ihr Engpass die Festplatte ist, ist es wichtig, wie Sie daraus lesen.
dd if=filename bs=128M | wc -l
ist viel schneller alswc -l filename
odercat filename | wc -l
für meinen Computer mit Festplatte und schneller CPU und RAM. Sie können mit der Blockgröße herumspielen und sehen, welchedd
Berichte als Durchsatz angezeigt werden. Ich drehte es auf 1GiB.Hinweis: Es gibt einige Debatten darüber, ob
cat
oderdd
es schneller ist . Ich behaupte nur, dassdd
es je nach System schneller gehen kann und dass es für mich ist. Probieren Sie es aus.quelle
Wenn Ihr Computer über Python verfügt, können Sie dies über die Shell versuchen:
Dies wird verwendet
python -c
, um einen Befehl zu übergeben, der im Grunde die Datei liest und durch die "neue Zeile" aufteilt, um die Anzahl der Zeilenumbrüche oder die Gesamtlänge der Datei zu erhalten.@ BlueMoon's :
Verwenden Sie die oben genannten:
quelle
python -c "print(sum(1 for line in open('text.txt'))"
wäre eine bessere Lösung in Python, da nicht die gesamte Datei in den Speicher eingelesen wird, sondern entweder sed oder wc eine viel bessere Lösung wäre.Ausgabe:
quelle
Lasst uns annehmen:
Dann möchten Sie die Dateien wirklich in Teile zerlegen, Teile auf mehreren Knoten parallel zählen und die Ergebnisse von dort zusammenfassen (dies ist im Grunde die Idee von @Chris White).
So machen Sie das mit GNU Parallel (Version> 20161222). Sie müssen die Knoten auflisten
~/.parallel/my_cluster_hosts
undssh
auf alle zugreifen können:Benutzen als:
quelle
Ich habe eine Textdatei mit 645 GB, und keine der früheren exakten Lösungen (z. B.
wc -l
) hat innerhalb von 5 Minuten eine Antwort zurückgegeben.Stattdessen gibt es hier ein Python-Skript, das die ungefähre Anzahl von Zeilen in einer großen Datei berechnet . (Meine Textdatei enthält anscheinend ungefähr 5,5 Milliarden Zeilen.) Das Python-Skript führt Folgendes aus:
A. Zählt die Anzahl der Bytes in der Datei.
B. Liest die ersten
N
Zeilen in der Datei (als Beispiel) und berechnet die durchschnittliche Zeilenlänge.C. Berechnet A / B als ungefähre Anzahl von Zeilen.
Es folgt der Linie von Nicos Antwort , berechnet aber nicht die Länge einer Zeile, sondern die durchschnittliche Länge der ersten
N
Zeilen.Hinweis: Ich gehe von einer ASCII-Textdatei aus, daher erwarte ich, dass die Python-
len()
Funktion die Anzahl der Zeichen als Anzahl der Bytes zurückgibt.Fügen Sie diesen Code in eine Datei ein
line_length.py
:#!/usr/bin/env python # Usage: # python line_length.py <filename> <N> import os import sys import numpy as np if __name__ == '__main__': file_name = sys.argv[1] N = int(sys.argv[2]) # Number of first lines to use as sample. file_length_in_bytes = os.path.getsize(file_name) lengths = [] # Accumulate line lengths. num_lines = 0 with open(file_name) as f: for line in f: num_lines += 1 if num_lines > N: break lengths.append(len(line)) arr = np.array(lengths) lines_count = len(arr) line_length_mean = np.mean(arr) line_length_std = np.std(arr) line_count_mean = file_length_in_bytes / line_length_mean print('File has %d bytes.' % (file_length_in_bytes)) print('%.2f mean bytes per line (%.2f std)' % (line_length_mean, line_length_std)) print('Approximately %d lines' % (line_count_mean))
Rufen Sie es so mit
N
= 5000 auf.Die Datei enthält also ungefähr 5,5 Milliarden Zeilen.
quelle