Почему 'tac файл | grep нечто' (передача по каналу) быстрее, чем 'grep нечто <<(tac файл)' (замена процесса)?

Текущий каталог (т.е. .) не находится в Вашем пути. Попробуйте

./startup.sh

Можно проверить путь с

echo ${PATH}

Вы могли добавить текущий каталог (.) к Вашему пути, но это считают риском (особенно, если . перед другими каталогами): при вводе команды оболочка сначала попытается выполнить его в текущем каталоге. Это выполнит то, что там вместо по умолчанию.

Суммирование: только запустите исполняемые файлы в текущем каталоге с ./ перед ними.

7
13.04.2017, 15:36
3 ответа

Конструкция <(tac file) заставляет оболочку:

  • Создать канал с именем
    • В таких системах, как Linux и SysV, которые имеют / dev / fd , используется обычный канал, а / dev / fd / <дескриптор-файла-канала > используется как имя.
    • В других системах используется именованный канал, который требует создания фактической записи файла на диске.
  • Запустите команду tac-файл и подключите его к одному концу трубы.
  • Заменить всю конструкцию в командной строке именем канала.

После замены командная строка становится такой:

grep whatever < /tmp/whatever-name-the-shell-used-for-the-named-pipe

И затем выполняется grep , и он считывает свой стандартный ввод (который является каналом), читает его и ищет свой первый аргумент в который.

Таким образом, конечный результат такой же, как и с ...

tac file | grep whatever

... в том, что запускаются те же две программы, и для их соединения по-прежнему используется канал. Но конструкция <(...) более запутанная, потому что она включает больше шагов и может включать временный файл (именованный канал).

Конструкция <(...) является расширением и недоступна в стандартной оболочке POSIX bourne или на платформах, не поддерживающих / dev / fd или именованных трубы. Только по этой причине, поскольку две рассматриваемые альтернативы в точности эквивалентны по функциональности, команда more portable | Форма other-command - лучший выбор.

Конструкция <(...) должна быть медленнее из-за дополнительной свертки, но это только на этапе запуска, и я не думаю, что разницу будет легко измерить.

ПРИМЕЧАНИЕ : На платформах Linux SysV <(...) не использует именованные каналы, а вместо этого использует обычные каналы. Обычные каналы (действительно, все файловые дескрипторы) могут упоминаться по специальному имени / dev / fd / <номер-дескриптора файла , так что это то, что оболочка использует в качестве имени канала. Таким образом, он избегает создания реального именованного канала с истинным временным именем файла в реальной файловой системе. Хотя трюк / dev / fd - это то, что использовалось для реализации этой функции, когда она изначально появилась в ksh , это оптимизация: на платформах, которые не поддерживают это, обычная именованный канал в реальной файловой системе используется, как описано выше.

ТАКЖЕ ПРИМЕЧАНИЕ : описание синтаксиса как << (...) вводит в заблуждение. На самом деле это <(...) , которое заменяется именем канала, а затем другой символ <, который ставит префикс всего этого, отделен от этого синтаксиса, и это обычный хорошо известный синтаксис для перенаправления ввода из файла.

10
27.01.2020, 20:15

В чем разница между | и << ()?

Между ними есть разница:

  • | вызывают выполнение каждой команды в отдельной подоболочке.

  • <() запустить команду, которая подставляется в фоновый режим.

Для следующих двух вопросов мы сделаем несколько strace :

pipe :

$ strace -fc bash -c 'tac /usr/share/dict/american-english | grep qwerty'
$ time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.008120        2707         3         1 wait4
  0.00    0.000000           0       352           read
  0.00    0.000000           0       229           write
  0.00    0.000000           0        20         2 open
  0.00    0.000000           0        29         2 close
  0.00    0.000000           0        40        17 stat
  0.00    0.000000           0        19           fstat
  0.00    0.000000           0       117           lseek
  0.00    0.000000           0        38           mmap
  0.00    0.000000           0        18           mprotect
  0.00    0.000000           0         6           munmap
  0.00    0.000000           0        25           brk
  0.00    0.000000           0        22           rt_sigaction
  0.00    0.000000           0        18           rt_sigprocmask
  0.00    0.000000           0         1           rt_sigreturn
  0.00    0.000000           0         3         2 ioctl
  0.00    0.000000           0        24        12 access
  0.00    0.000000           0         1           pipe
  0.00    0.000000           0         2           dup2
  0.00    0.000000           0         1           getpid
  0.00    0.000000           0         1         1 getpeername
  0.00    0.000000           0         2           clone
  0.00    0.000000           0         3           execve
  0.00    0.000000           0         1           uname
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0        13           getuid
  0.00    0.000000           0        13           getgid
  0.00    0.000000           0        13           geteuid
  0.00    0.000000           0        13           getegid
  0.00    0.000000           0         1           getppid
  0.00    0.000000           0         1           getpgrp
  0.00    0.000000           0         3           arch_prctl
  0.00    0.000000           0         1           time
------ ----------- ----------- --------- --------- ----------------
100.00    0.008120                  1034        37 total

Process Substitution :

$ strace -fc bash -c 'grep qwerty < <(tac /usr/share/dict/american-english)'
$ time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 99.14    0.016001        4000         4         2 wait4
  0.46    0.000075           0       229           write
  0.24    0.000038           0       341           read
  0.16    0.000026           1        24           brk
  0.00    0.000000           0        21         2 open
  0.00    0.000000           0        27           close
  0.00    0.000000           0        40        17 stat
  0.00    0.000000           0        19           fstat
  0.00    0.000000           0       117           lseek
  0.00    0.000000           0        38           mmap
  0.00    0.000000           0        18           mprotect
  0.00    0.000000           0         6           munmap
  0.00    0.000000           0        35           rt_sigaction
  0.00    0.000000           0        24           rt_sigprocmask
  0.00    0.000000           0         2           rt_sigreturn
  0.00    0.000000           0         3         2 ioctl
  0.00    0.000000           0        24        12 access
  0.00    0.000000           0         1           pipe
  0.00    0.000000           0         3           dup2
  0.00    0.000000           0         1           getpid
  0.00    0.000000           0         1         1 getpeername
  0.00    0.000000           0         3           clone
  0.00    0.000000           0         3           execve
  0.00    0.000000           0         1           uname
  0.00    0.000000           0         1         1 fcntl
  0.00    0.000000           0         2           getrlimit
  0.00    0.000000           0        13           getuid
  0.00    0.000000           0        13           getgid
  0.00    0.000000           0        13           geteuid
  0.00    0.000000           0        13           getegid
  0.00    0.000000           0         1           getppid
  0.00    0.000000           0         1           getpgrp
  0.00    0.000000           0         3           arch_prctl
  0.00    0.000000           0         1           time
------ ----------- ----------- --------- --------- ----------------
100.00    0.016140                  1046        37 total

Почему что-то быстрее, чем другое?

А что на самом деле быстрее?

Как видите, подстановка процесса в этом случае медленнее, чем pipe , потому что она использует больше системных вызовов. Оба тратят много времени на ожидание дочерних процессов, но подстановка процесса использует больше системного вызова wait4 () и использует больше времени для каждого вызова, чем pipe .

Почему никто не предлагает xargs?

Я не думаю, что xargs может здесь чем-то помочь, это не его работа.

Обновление

Как предложил @ Gilles, я провожу тест с большим файлом, 2 ГБ случайных данных, сгенерированных из / dev / urandom . Это показывает, что pipe действительно быстрее, чем замещение процесса .

труба :

$ strace -fc bash -c 'tac sample.txt | grep qwerty'
$ time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 81.15    8.284959     2761653         3         1 wait4
 17.89    1.825959           2    780959           read
  0.91    0.092708           0    524286           write
  0.05    0.005364           0    262146           lseek
  0.00    0.000000           0        20         2 open
  0.00    0.000000           0        29         2 close
  0.00    0.000000           0        40        17 stat
  0.00    0.000000           0        19           fstat
  0.00    0.000000           0        38           mmap
  0.00    0.000000           0        18           mprotect
  0.00    0.000000           0         6           munmap
  0.00    0.000000           0        25           brk
  0.00    0.000000           0        22           rt_sigaction
  0.00    0.000000           0        18           rt_sigprocmask
  0.00    0.000000           0         1           rt_sigreturn
  0.00    0.000000           0         3         2 ioctl
  0.00    0.000000           0        24        12 access
  0.00    0.000000           0         1           pipe
  0.00    0.000000           0         2           dup2
  0.00    0.000000           0         1           getpid
  0.00    0.000000           0         1         1 getpeername
  0.00    0.000000           0         2           clone
  0.00    0.000000           0         3           execve
  0.00    0.000000           0         1           uname
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0        13           getuid
  0.00    0.000000           0        13           getgid
  0.00    0.000000           0        13           geteuid
  0.00    0.000000           0        13           getegid
  0.00    0.000000           0         1           getppid
  0.00    0.000000           0         1           getpgrp
  0.00    0.000000           0         3           arch_prctl
  0.00    0.000000           0         1           time
------ ----------- ----------- --------- --------- ----------------
100.00   10.208990               1567727        37 total

подстановка процесса :

$ strace -fc bash -c 'grep qwerty < <(tac sample.txt)'
$ time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 99.51   13.912869     3478217         4         2 wait4
  0.38    0.053373           0    655269           read
  0.09    0.013084           0    524286           write
  0.02    0.002454           0    262146           lseek
  0.00    0.000030           1        38           mmap
  0.00    0.000024           1        24        12 access
  0.00    0.000000           0        21         2 open
  0.00    0.000000           0        27           close
  0.00    0.000000           0        40        17 stat
  0.00    0.000000           0        19           fstat
  0.00    0.000000           0        18           mprotect
  0.00    0.000000           0         6           munmap
  0.00    0.000000           0        24           brk
  0.00    0.000000           0        35           rt_sigaction
  0.00    0.000000           0        24           rt_sigprocmask
  0.00    0.000000           0         2           rt_sigreturn
  0.00    0.000000           0         3         2 ioctl
  0.00    0.000000           0         1           pipe
  0.00    0.000000           0         3           dup2
  0.00    0.000000           0         1           getpid
  0.00    0.000000           0         1         1 getpeername
  0.00    0.000000           0         3           clone
  0.00    0.000000           0         3           execve
  0.00    0.000000           0         1           uname
  0.00    0.000000           0         1         1 fcntl
  0.00    0.000000           0         2           getrlimit
  0.00    0.000000           0        13           getuid
  0.00    0.000000           0        13           getgid
  0.00    0.000000           0        13           geteuid
  0.00    0.000000           0        13           getegid
  0.00    0.000000           0         1           getppid
  0.00    0.000000           0         1           getpgrp
  0.00    0.000000           0         3           arch_prctl
  0.00    0.000000           0         1           time
------ ----------- ----------- --------- --------- ----------------
100.00   13.981834               1442060        37 total
7
27.01.2020, 20:15

Мне не удалось воспроизвести результаты, показанные cuonglm . Даже с файлом размером 2 ГБ в Bash 5 на MacOS Mojave я вижу очень похожие тайминги между заменой процесса и каналом. Это имеет смысл для меня, так как накладные расходы, связанные с вызовом, будут минимальными по сравнению с фактической обработкой этого вызова для файла размером 2 ГБ, поэтому выполнение одной итерации использования подстановки процесса вместо канала будет отключено. к случайности / какая команда была запущена первой для кэширования содержимого файла.

Мне удалось воспроизвести выводы в этом вопросе , которые показывают, что подстановка процессов выполняется быстрее, чем конвейеры при нескольких тысячах вызовов.

Вот команды, которые я выполнил, и вывод:

труб.ш:

shopt -s lastpipe
for i in {1..5000}; do
    echo foo bar |
    while read; do
        echo $REPLY >/dev/null
    done
done

проц -под.ш:

for i in {1..5000}; do
    while read; do
        echo $REPLY >/dev/null
    done < <(echo foo bar)
done

труба -нет -ластпайп.ш:

for i in {1..5000}; do
    echo foo bar |
    while read; do
        echo $REPLY >/dev/null
    done
done

Тест:

time./proc-sub.sh

real    0m9.505s
user    0m1.875s
sys     0m10.705s

time./pipe.sh

real    0m14.036s
user    0m4.583s
sys     0m14.193s

time./pipe-no-lastpipe.sh

real    0m16.696s
user    0m3.055s
sys     0m18.057s
1
09.04.2020, 22:07

Теги

Похожие вопросы