¿Por qué el archivo echo> usa más tiempo real que echo | sed> file?

28

El ejemplo a continuación me sorprendió. Parece ser contrario a la intuición ... aparte del hecho de que hay un bigote más tiempo de usuario para el echo | sedcombo.

¿Por qué está echousando tanto tiempo del sistema cuando se ejecuta solo, o debería ser la pregunta, ¿Cómo sedcambia el estado de juego? Parece que echotendría que hacer el mismo eco en ambos casos ...

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
fuente
1
Mi reacción intestinal es que tiene que ver con el almacenamiento en búfer.
bahamat
1
@bahamat, creo que tienes razón. El eco hace una escritura separada () para cada argumento. El sed los amortigua. Entonces, la primera versión tiene un millón de escrituras en un archivo normal, pasando por un controlador de sistema de archivos en la capa del dispositivo de bloque, y la segunda versión tiene un millón de escrituras entrando en una tubería y un poco menos de escrituras pasando por las capas más caras del código del núcleo.
Alan Curry
@bahamat Definitivamente buffering. time echo ... |cat >filee incluso time echo ... |perl -ne 'print'son tiempos similares a la sedversión.
StarNamer
44
Gracias a todos por las buenas explicaciones ... Entonces, para grandes escrituras de varias líneas (en bash), cat ha obtenido un punto de Uso útil del gato :)
Peter.O

Respuestas:

29

bahamat y Alan Curry tienen razón: esto se debe a la forma en que su shell amortigua la salida de echo. Específicamente, su shell es bash y emite una writellamada al sistema por línea. Por lo tanto, el primer fragmento realiza 1000000 escrituras en un archivo de disco, mientras que el segundo fragmento realiza 1000000 escrituras en una tubería y sed (en gran parte en paralelo, si tiene varias CPU) hace un número considerablemente menor de escrituras en un archivo de disco debido a su salida almacenamiento en búfer.

Puedes observar lo que está sucediendo corriendo 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) ---

Otros shells como ksh amortiguan la salida echoincluso cuando es multilínea, por lo que no verá mucha diferencia.

$ 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

Con bash obtengo proporciones de tiempo similares. Con ksh veo que el segundo fragmento se ejecuta más lentamente.

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- deja de ser malvado'
fuente
Gracias ... Ese último kshejemplo es más
parecido a