Почему echo> файл использует больше реального времени, чем echo | sed> file?

28

Пример ниже удивил меня. Кажется, это противоречит интуиции ... кроме того факта, что у комбо больше времени для пользователяecho | sed .

Почему echoиспользуется так много системного времени, когда он работает один, или должен быть вопрос: как sedменяется состояние игры? Похоже, что в обоих случаях echoнужно повторить одно и то же ...

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
источник
1
Моя внутренняя реакция - это связано с буферизацией.
Багамат
1
@ bahamat Я думаю, ты прав. Echo выполняет отдельную запись () для каждого аргумента. Сед буферизует их. Таким образом, первая версия имеет миллион записей в обычный файл, проходящий через драйвер файловой системы на уровень блочных устройств, а вторая версия имеет миллион записей, поступающих в канал, и несколько меньшее количество записей, проходящих через более дорогие слои кода ядра.
Алан Карри
@bahamat Определенно буферизация. time echo ... |cat >fileи даже time echo ... |perl -ne 'print'времена похожи на sedверсию.
StarNamer
4
Спасибо всем за хорошие объяснения ... Так что за большие многострочные записи (в bash), cat получил полезное использование точки Cat :)
Peter.O

Ответы:

29

bahamat и Alan Curry правильно поняли: это связано с тем, как ваша оболочка буферизует вывод echo. В частности, ваша оболочка является bash и выдает один writeсистемный вызов на строку. Следовательно, первый фрагмент выполняет 1000000 операций записи в файл на диске, тогда как второй фрагмент выполняет 1000000 операций записи в канал, а sed (в основном параллельно, если у вас несколько ЦП) выполняет значительно меньшее количество операций записи в файл диска из-за его вывода. буферизация.

Вы можете наблюдать, что происходит, запустив 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) ---

Другие оболочки, такие как ksh, буферизуют вывод echoдаже тогда, когда он многострочный, поэтому вы не увидите большой разницы.

$ 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

С bash я получаю аналогичные временные соотношения. С помощью ksh я вижу, что второй фрагмент работает медленнее.

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
Жиль "ТАК - перестань быть злым"
источник
Спасибо ... Этот последний kshпример больше соответствует тому, что я ожидал ...
Peter.O