In einem bash
Skript benötige ich verschiedene Werte aus /proc/
Dateien. Bis jetzt habe ich Dutzende von Zeilen, die die Dateien direkt so abfassen:
grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo
Um dies effizienter zu gestalten, habe ich den Dateiinhalt in einer Variablen gespeichert und Folgendes angegeben:
a=$(</proc/meminfo)
echo "$a" | grep -oP '^MemFree: *\K[0-9]+'
Anstatt die Datei mehrmals zu öffnen, sollte sie nur einmal geöffnet werden und der variable Inhalt, von dem ich angenommen habe, dass er schneller ist, wird jedoch langsamer:
bash 4.4.19 $ time for i in {1..1000};do grep ^MemFree /proc/meminfo;done >/dev/null
real 0m0.803s
user 0m0.619s
sys 0m0.232s
bash 4.4.19 $ a=$(</proc/meminfo)
bash 4.4.19 $ time for i in {1..1000};do echo "$a"|grep ^MemFree; done >/dev/null
real 0m1.182s
user 0m1.425s
sys 0m0.506s
Gleiches gilt für dash
und zsh
. Ich habe den besonderen Status von /proc/
Dateien als Grund vermutet , aber wenn ich den Inhalt /proc/meminfo
in eine reguläre Datei kopiere und das Ergebnis als dasselbe bezeichne:
bash 4.4.19 $ cat </proc/meminfo >meminfo
bash 4.4.19 $ time for i in $(seq 1 1000);do grep ^MemFree meminfo; done >/dev/null
real 0m0.790s
user 0m0.608s
sys 0m0.227s
Durch die Verwendung eines Here-Strings zum Speichern der Pipe wird sie etwas schneller, aber immer noch nicht so schnell wie bei den Dateien:
bash 4.4.19 $ time for i in $(seq 1 1000);do <<<"$a" grep ^MemFree; done >/dev/null
real 0m0.977s
user 0m0.758s
sys 0m0.268s
Warum wird eine Datei schneller geöffnet als derselbe Inhalt aus einer Variablen gelesen?
/proc/
Dateien als Grund vermutet , aber wenn ich den Inhalt/proc/meminfo
einer regulären Datei kopiere und benutze, sind die Ergebnisse die gleichen:" Es ist nichts Besonderes für/proc/
Dateien, das Lesen von regulären Dateien ist auch schneller!Antworten:
Hier geht es nicht um das Öffnen einer Datei im Vergleich zu einer Variablen Inhalt zu lesen , sondern mehr über einen zusätzlichen Prozess Forking oder nicht.
grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo
Forks Ein Prozess, der ausgeführt wird undgrep
geöffnet wird/proc/meminfo
(eine virtuelle Datei im Arbeitsspeicher, ohne dass Festplatten-E / A betroffen sind), liest sie und stimmt mit dem regulären Ausdruck überein.Der teuerste Teil dabei ist, den Prozess zu forken und das grep-Dienstprogramm und seine Bibliotheksabhängigkeiten zu laden, die dynamische Verknüpfung durchzuführen, die Gebietsschemadatenbank zu öffnen und Dutzende von Dateien auf der Festplatte (aber wahrscheinlich im Arbeitsspeicher zwischengespeichert).
Der Teil über das Lesen
/proc/meminfo
ist im Vergleich unbedeutend, der Kernel benötigt wenig Zeit, um die darin enthaltenen Informationen zu generieren, undgrep
benötigt wenig Zeit, um sie zu lesen.Wenn Sie
strace -c
damit arbeiten, werden Sie feststellen, dass der eineopen()
und der andereread()
Systemaufruf, der zum Lesen verwendet/proc/meminfo
wird, Erdnüsse ist, im Vergleich zu allem, wasgrep
zum Startenstrace -c
erforderlich ist ( ohne Gabelung).Im:
In den meisten Shells, die diesen
$(<...)
ksh-Operator unterstützen, öffnet die Shell nur die Datei und liest ihren Inhalt (und entfernt die nachfolgenden Zeilenumbrüche).bash
ist anders und viel weniger effizient, da es einen Prozess zum Lesen auffordert und die Daten über eine Pipe an das übergeordnete Element weiterleitet. Aber hier ist es einmal gemacht, also spielt es keine Rolle.Im:
Die Shell muss zwei Prozesse erzeugen , die gleichzeitig ablaufen, aber über eine Pipe miteinander interagieren. Das Erstellen, Abreißen und Schreiben und Lesen von Pfeifen ist mit geringen Kosten verbunden. Die weitaus höheren Kosten entstehen durch einen zusätzlichen Prozess. Die Planung der Prozesse hat ebenfalls einen Einfluss.
Möglicherweise können Sie feststellen, dass die Verwendung des
<<<
Operators zsh den Vorgang etwas beschleunigt:In zsh und bash geschieht dies durch Schreiben des Inhalts
$a
in eine temporäre Datei. Dies ist kostengünstiger als das Erstellen eines zusätzlichen Prozesses, bietet jedoch wahrscheinlich keinen Gewinn im Vergleich zum direkten Abrufen der Daten/proc/meminfo
. Das ist immer noch weniger effizient als Ihr Ansatz, der/proc/meminfo
auf die Festplatte kopiert , da das Schreiben der temporären Datei bei jeder Iteration erfolgt.dash
Unterstützt keine Here-Strings, aber seine Heredocs werden mit einer Pipe implementiert, die keinen zusätzlichen Prozess beinhaltet. Im:Die Shell erzeugt eine Pipe, gabelt einen Prozess. Das untergeordnete Element wird
grep
mit der Standardeingabe als Leseende der Pipe ausgeführt, und das übergeordnete Element schreibt den Inhalt am anderen Ende der Pipe.Das Pipehandling und die Prozesssynchronisation sind jedoch wahrscheinlich immer noch teurer, als nur die Daten direkt abzurufen
/proc/meminfo
.Der Inhalt von
/proc/meminfo
ist kurz und braucht nicht viel Zeit, um zu produzieren. Wenn Sie einige CPU-Zyklen einsparen möchten, möchten Sie die teuren Teile entfernen: Prozesse forken und externe Befehle ausführen.Mögen:
Vermeiden Sie
bash
jedoch, dass die Musterübereinstimmung sehr unzureichend ist. Mitzsh -o extendedglob
können Sie es verkürzen auf:Beachten Sie, dass dies
^
in vielen Shells besonders ist (Bourne, fish, rc, es und zsh mindestens mit der Option extendedglob). Ich würde empfehlen, es zu zitieren. Beachten Sie auch, dassecho
nicht zur Ausgabe beliebiger Daten verwendet werden kann (daher meine Verwendung vonprintf
oben).quelle
printf
Sie sagen, dass die Shell zwei Prozesseprintf
erzeugen muss , aber keine eingebaute Shell ist?grep
und führt aus.A | B
, gibt es einige Shells wie AT & T ksh oder zsh, dieB
im aktuellen Shell-Prozess ausgeführt werden, wenn es ein eingebauter oder zusammengesetzter Befehl oder ein Funktionsbefehl ist, ich kenne keinen, derA
im aktuellen Prozess ausgeführt wird. Wenn überhaupt, müssten sie SIGPIPE auf eine komplexe Art und Weise behandeln, als obA
sie im untergeordneten Prozess ausgeführt würden, und ohne die Shell zu beenden, damit das Verhalten beimB
vorzeitigen Beenden nicht zu überraschend ist . Es ist viel einfacher,B
im übergeordneten Prozess auszuführen .<<<
bash
nicht unterstützt<<<
, nur, dass der Operator kamzsh
wie$(<...)
kam von ksh.In Ihrem ersten Fall verwenden Sie einfach das grep-Dienstprogramm und suchen etwas aus einer Datei
/proc/meminfo
. Es/proc
ist ein virtuelles Dateisystem, sodass sich die/proc/meminfo
Datei im Speicher befindet und nur sehr wenig Zeit zum Abrufen des Inhalts benötigt wird.Im zweiten Fall erstellen Sie jedoch eine Pipe und übergeben die Ausgabe des ersten Befehls mithilfe dieser Pipe an den zweiten Befehl. Dies ist sehr kostspielig.
Der Unterschied liegt an
/proc
(weil es sich im Speicher befindet) und an der Pipe (siehe Beispiel unten):quelle
In beiden Fällen rufen Sie einen externen Befehl auf (grep). Für den externen Anruf ist eine Unterschale erforderlich. Das Gabeln dieser Schale ist die fundamentale Ursache für die Verzögerung. Beide Fälle sind ähnlich, also: eine ähnliche Verzögerung.
Wenn Sie die externe Datei nur einmal lesen und (von einer Variablen) mehrmals verwenden möchten, verlassen Sie die Shell nicht:
Das dauert nur etwa 0,1 Sekunden anstatt der vollen 1 Sekunde für den grep-Aufruf.
quelle