Ich habe eine einzigartige Situation, in der ich die auf dieser Seite vorgeschlagenen Lösungen vergleichen kann. Daher schreibe ich diese Antwort als Konsolidierung der vorgeschlagenen Lösungen mit jeweils enthaltenen Laufzeiten.
Installieren
Ich habe eine 3,261-Gigabyte-ASCII-Textdatendatei mit einem Schlüssel-Wert-Paar pro Zeile. Die Datei enthält insgesamt 3.339.550.320 Zeilen und kann nicht in jedem Editor geöffnet werden, den ich ausprobiert habe, einschließlich meines Go-to-Vim. Ich muss diese Datei unterteilen, um einige der Werte zu untersuchen, die ich entdeckt habe. Beginnen Sie erst bei Zeile ~ 500.000.000.
Weil die Datei so viele Zeilen hat:
- Ich muss nur eine Teilmenge der Zeilen extrahieren, um etwas Nützliches mit den Daten zu tun.
- Das Lesen jeder Zeile, die zu den Werten führt, die mir wichtig sind, wird lange dauern.
- Wenn die Lösung über die Zeilen hinaus liest, die mir wichtig sind, und den Rest der Datei weiter liest, verschwendet sie Zeit mit dem Lesen von fast 3 Milliarden irrelevanten Zeilen und dauert 6x länger als nötig.
Mein Best-Case-Szenario ist eine Lösung, die nur eine einzelne Zeile aus der Datei extrahiert, ohne eine der anderen Zeilen in der Datei zu lesen, aber ich kann mir nicht vorstellen, wie ich dies in Bash erreichen würde.
Aus Gründen meiner geistigen Gesundheit werde ich nicht versuchen, die gesamten 500.000.000 Zeilen zu lesen, die ich für mein eigenes Problem benötige. Stattdessen werde ich versuchen, Zeile 50.000.000 aus 3.339.550.320 zu extrahieren (was bedeutet, dass das Lesen der vollständigen Datei 60x länger dauert als nötig).
Ich werde das time
eingebaute verwenden, um jeden Befehl zu bewerten.
Basislinie
Lassen Sie uns zuerst sehen, wie die head
tail
Lösung:
$ time head -50000000 myfile.ascii | tail -1
pgm_icnt = 0
real 1m15.321s
Die Basis für Zeile 50 Millionen ist 00: 01: 15.321. Wenn ich direkt für Zeile 500 Millionen gegangen wäre, wären es wahrscheinlich ~ 12,5 Minuten.
Schnitt
Ich bin zweifelhaft, aber es ist einen Versuch wert:
$ time cut -f50000000 -d$'\n' myfile.ascii
pgm_icnt = 0
real 5m12.156s
Dieser dauerte 00: 05: 12.156, was viel langsamer als die Grundlinie ist! Ich bin mir nicht sicher, ob es die gesamte Datei durchliest oder nur bis zu 50 Millionen Zeilen vor dem Stoppen, aber unabhängig davon scheint dies keine praktikable Lösung für das Problem zu sein.
AWK
Ich habe die Lösung nur mit ausgeführt, exit
weil ich nicht auf die Ausführung der vollständigen Datei warten wollte:
$ time awk 'NR == 50000000 {print; exit}' myfile.ascii
pgm_icnt = 0
real 1m16.583s
Dieser Code lief in 00: 01: 16.583, was nur ~ 1 Sekunde langsamer ist, aber immer noch keine Verbesserung der Basislinie darstellt. Bei dieser Geschwindigkeit hätte das Lesen der gesamten Datei wahrscheinlich ungefähr 76 Minuten gedauert, wenn der Exit-Befehl ausgeschlossen worden wäre!
Perl
Ich habe auch die vorhandene Perl-Lösung ausgeführt:
$ time perl -wnl -e '$.== 50000000 && print && exit;' myfile.ascii
pgm_icnt = 0
real 1m13.146s
Dieser Code wurde in 00: 01: 13.146 ausgeführt, was ~ 2 Sekunden schneller als die Basislinie ist. Wenn ich es mit den vollen 500.000.000 laufen lassen würde, würde es wahrscheinlich ~ 12 Minuten dauern.
sed
Die beste Antwort an der Tafel, hier ist mein Ergebnis:
$ time sed "50000000q;d" myfile.ascii
pgm_icnt = 0
real 1m12.705s
Dieser Code wurde in 00: 01: 12.705 ausgeführt, was 3 Sekunden schneller als die Basislinie und ~ 0,4 Sekunden schneller als Perl ist. Wenn ich es auf den vollen 500.000.000 Zeilen ausführen würde, hätte es wahrscheinlich ~ 12 Minuten gedauert.
Mapfile
Ich habe Bash 3.1 und kann daher die Mapfile-Lösung nicht testen.
Fazit
Es sieht zum größten Teil so aus, als ob es schwierig ist, die head
tail
Lösung zu verbessern . Bestenfalls sed
bietet die Lösung eine Effizienzsteigerung von ~ 3%.
(Prozentsätze berechnet mit der Formel % = (runtime/baseline - 1) * 100
)
Zeile 50.000.000
- 00: 01: 12.705 (-00: 00: 02.616 = -3.47%)
sed
- 00: 01: 13.146 (-00: 00: 02.175 = -2,89%)
perl
- 00: 01: 15.321 (+00: 00: 00.000 = + 0,00%)
head|tail
- 00: 01: 16.583 (+00: 00: 01.262 = + 1.68%)
awk
- 00: 05: 12.156 (+00: 03: 56.835 = + 314,43%)
cut
Zeile 500.000.000
- 00: 12: 07.050 (-00: 00: 26.160)
sed
- 00: 12: 11.460 (-00: 00: 21.750)
perl
- 00: 12: 33.210 (+00: 00: 00.000)
head|tail
- 00: 12: 45.830 (+00: 00: 12.620)
awk
- 00: 52: 01.560 (+00: 40: 31.650)
cut
Zeile 3.338.559.320
- 01: 20: 54.599 (-00: 03: 05.327)
sed
- 01: 21: 24.045 (-00: 02: 25.227)
perl
- 01: 23: 49.273 (+00: 00: 00.000)
head|tail
- 01: 25: 13.548 (+00: 02: 35.735)
awk
- 05: 47: 23.026 (+04: 24: 26.246)
cut
awk
undsed
und ich bin sicher, dass jemand auch einen Perl-