Pourquoi echo> file utilise-t-il plus de temps réel que echo | sed> fichier?

28

L'exemple ci-dessous m'a surpris. Cela semble être contre-intuitif ... à part le fait qu'il y a un temps d'utilisation plus important pour le echo | sedcombo.

Pourquoi echoutilise-t-on autant de temps système lorsqu'il fonctionne seul, ou la question devrait-elle être: comment sedchange-t-on l'état des lieux? Il semble qu'il echofaudrait faire le même écho dans les deux cas ...

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
la source
1
Ma réaction intestinale est liée à la mise en mémoire tampon.
bahamat
1
@bahamat Je pense que vous avez raison. L'écho fait un write () distinct pour chaque argument. Le sed les tamponne. Ainsi, la première version a un million d'écritures dans un fichier normal, passant par un pilote de système de fichiers dans la couche de périphérique de bloc, et la deuxième version a un million d'écritures entrant dans un canal et un peu moins d'écritures passant par les couches plus chères du code du noyau.
Alan Curry
@bahamat Buffer définitivement. time echo ... |cat >fileet time echo ... |perl -ne 'print'sont même des temps similaires à la sedversion.
StarNamer
4
Merci à tous pour les bonnes explications ... Donc, pour les grandes écritures multi-lignes (en bash), cat a gagné un point d'utilisation utile de Cat :)
Peter.O

Réponses:

29

bahamat et Alan Curry ont raison: cela est dû à la façon dont votre shell met en mémoire tampon la sortie de echo. Plus précisément, votre shell est bash et émet un writeappel système par ligne. Par conséquent, le premier extrait fait 1000000 écritures sur un fichier disque, tandis que le deuxième extrait fait 1000000 écritures sur un canal et sed (en grande partie en parallèle, si vous avez plusieurs processeurs) fait un nombre considérablement plus faible d'écritures sur un fichier disque en raison de sa sortie mise en mémoire tampon.

Vous pouvez observer ce qui se passe en exécutant 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) ---

D'autres shells tels que ksh tamponnent la sortie de echomême quand il est multiligne, donc vous ne verrez pas beaucoup de différence.

$ 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

Avec bash, j'obtiens des rapports de synchronisation similaires. Avec ksh, je vois le deuxième extrait fonctionner plus lentement.

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- arrête d'être méchant'
la source
Merci ... Ce dernier kshexemple correspond plus à ce que j'attendais ...
Peter.O