Por que o arquivo echo> usa mais tempo real do que o eco | sed> arquivo?

28

O exemplo abaixo me surpreendeu. Parece ser contra-intuitivo ... além do fato de que há muito mais tempo de uso para o echo | sedcombo.

Por que está echousando tanto tempo do sistema quando é executado sozinho, ou deveria ser a pergunta: como sedmuda o estado do jogo? Parece que echoprecisaria fazer o mesmo eco nos dois 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
fonte
1
Minha reação intestinal é que tem a ver com buffer.
bahamat
1
@ Bahamat Eu acho que você está certo. O eco faz um write () separado para cada argumento. O sed amortece-los. Portanto, a primeira versão possui um milhão de gravações em um arquivo normal, passando por um driver de sistema de arquivos na camada de dispositivo de bloco, e a segunda versão tem um milhão de gravações em um pipe e um pouco menos de gravações nas camadas mais caras do código do kernel.
27612 Alan
@bahamat Definitivamente buffer. time echo ... |cat >filee até time echo ... |perl -ne 'print'são tempos semelhantes à sedversão.
StarNamer
4
Obrigado a todos pelas boas explicações ... Então para gravações multi-linha grandes (em bash), gato ganhou um uso útil do ponto de Cat :)
Peter.O

Respostas:

29

bahamat e Alan Curry estão certos: isso se deve à maneira como o shell armazena a saída echo. Especificamente, seu shell é bash e emite uma writechamada de sistema por linha. Portanto, o primeiro fragmento faz 1000000 gravações em um arquivo de disco, enquanto o segundo fragmento faz 1000000 gravações em um pipe e sed (principalmente em paralelo, se você tiver várias CPUs) faz um número consideravelmente menor de gravações em um arquivo de disco devido à sua saída carregando.

Você pode observar o que está acontecendo executando 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) ---

Outros shells, como o ksh, protegem a saída echomesmo quando ela é multilinha, para que você não veja muita diferença.

$ 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

Com o bash, recebo proporções de tempo semelhantes. Com o ksh, vejo o segundo trecho mais lento.

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- parar de ser mau'
fonte
Graças ... Esse último kshexemplo é mais ao longo das linhas do que eu esperava ...
Peter.O