Warum verwendet echo> file mehr Echtzeit als echo | sed> file?

28

Das folgende Beispiel hat mich überrascht. Es scheint kontraintuitiv zu sein ... abgesehen von der Tatsache, dass ein Whisker mehr Benutzerzeit für die echo | sedCombo hat.

Warum verbraucht sysecho so viel Zeit, wenn es alleine läuft, oder sollte sich die Frage stellen, wie sich sedder Stand der Dinge ändert ? Es scheint, dass in beiden Fällen echodas gleiche Echo erfolgen müsste ...

time echo -n a\ {1..1000000}\ c$'\n' >file

# real    0m9.481s
# user    0m5.304s
# sys     0m4.172s

time echo -n a\ {1..1000000}\ c$'\n' |sed s/^\ // >file

# real    0m5.955s
# user    0m5.488s
# sys     0m1.580s
Peter.O
quelle
1
Meiner Meinung nach hat es mit Pufferung zu tun.
Bahamat
1
@ Bahamat Ich denke, du hast Recht. Das Echo führt für jedes Argument ein separates write () aus. Das Sed puffert sie. Die erste Version enthält also eine Million Schreibvorgänge in eine reguläre Datei, die über einen Dateisystemtreiber in die Block-Device-Schicht übertragen werden. Die zweite Version enthält eine Million Schreibvorgänge in eine Pipe und etwas weniger Schreibvorgänge in die teureren Schichten des Kernel-Codes.
Alan Curry
@ Bahamat Auf jeden Fall Pufferung. time echo ... |cat >fileund sind sogar time echo ... |perl -ne 'print'mal der sedversion ähnlich .
StarNamer
4
Vielen Dank an alle für die guten Erklärungen ... Für große mehrzeilige Schreibvorgänge (in Bash) hat cat einen nützlichen Verwendungszweck für Cat-Punkte erhalten :)
Peter.O

Antworten:

29

Bahamat und Alan Curry haben Recht: Dies liegt an der Art und Weise, wie Ihre Shell die Ausgabe von puffert echo. Insbesondere handelt es sich bei Ihrer Shell um eine Bash-Shell, die einen writeSystemaufruf pro Zeile ausgibt . Daher schreibt das erste Snippet 1000000 in eine Festplattendatei, während das zweite Snippet 1000000 in eine Pipe schreibt und sed (bei mehreren CPUs weitgehend parallel) aufgrund seiner Ausgabe eine erheblich geringere Anzahl von Schreibvorgängen in eine Festplattendatei durchführt Pufferung.

Sie können beobachten , was durch Laufen ist los Strace .

$ strace -f -e write bash -c 'echo -n a\ {1..2}\ c$'\'\\n\'' >file'
write(1, "a 1 c\n", 6)                  = 6
write(1, " a 2 c\n", 7)                 = 7
$ strace -f -e write bash -c 'echo -n a\ {1..2}\ c$'\'\\n\'' | sed "s/^ //" >file'
Process 28052 attached
Process 28053 attached
Process 28051 suspended
[pid 28052] write(1, "a 1 c\n", 6)      = 6
[pid 28052] write(1, " a 2 c\n", 7)     = 7
Process 28051 resumed
Process 28052 detached
Process 28051 suspended
[pid 28053] write(1, "a 1 c\na 2 c\n", 12) = 12
Process 28051 resumed
Process 28053 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

Andere Shells wie ksh puffern die Ausgabe echoauch dann, wenn sie mehrzeilig ist, sodass Sie keinen großen Unterschied feststellen können.

$ strace -f -e write ksh -c 'echo -n a\ {1..2}\ c$'\'\\n\'' >file'
write(1, "a 1 c\n a 2 c\n", 13)         = 13
$ strace -f -e write ksh -c 'echo -n a\ {1..2}\ c$'\'\\n\'' | sed "s/^ //" >file'
Process 28058 attached
[pid 28058] write(1, "a 1 c\n a 2 c\n", 13) = 13
Process 28058 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
write(1, "a 1 c\na 2 c\n", 12)          = 12

Mit Bash bekomme ich ähnliche Timing-Verhältnisse. Mit ksh sehe ich das zweite Snippet langsamer laufen.

ksh$ time echo -n a\ {1..1000000}\ c$'\n' >file

real    0m1.44s
user    0m1.28s
sys     0m0.06s
ksh$ time echo -n a\ {1..1000000}\ c$'\n' | sed "s/^ //" >file

real    0m2.38s
user    0m1.52s
sys     0m0.14s
Gilles 'SO - hör auf böse zu sein'
quelle
Danke ... Das letzte kshBeispiel entspricht eher
meinen