パイプラインとプロセス代替のパフォーマンスの違い

パイプラインとプロセス代替のパフォーマンスの違い

ほとんどの場合、特に複数のコマンドセットで作業するときは、bashスクリプトでプロセス置換の代わりにパイプを使用する傾向が... | ... | ...あります... < <(... < <(...))

場合によっては、プロセス置換を使用することがパイプを使用するよりもはるかに速い理由があると思います。

これをテストするために、time同じ追加コマンドを繰り返して2つのスクリプトを作成しました10000。 1つはパイプを使用し、もう1つはプロセス置換を使用しました。

スクリプト:

pipeline.bash:

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

proc-sub.bash

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

結果:

~$ time ./pipeline.bash

real    0m17.678s
user    0m14.666s
sys     0m14.807s

~$ time ./proc-sub.bash

real    0m8.479s
user    0m4.649s
sys     0m6.358s

プロセスの置き換えが名前付きパイプまたは一部のファイルを生成している間にパイプが子プロセスを生成することを知っていますが、/dev/fdこれらの違いがパフォーマンスにどのような影響を与えるかは不明です。

ベストアンサー1

同じことを行うstraceと、違いを確認できます。

そしてpipe

$ strace -c ./pipe.sh 
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 57.89    0.103005           5     20000           clone
 40.81    0.072616           2     30000     10000 wait4
  0.58    0.001037           0    120008           rt_sigprocmask
  0.40    0.000711           0     10000           pipe

そしてproc-sub

$ strace -c ./procsub.sh 
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 85.08    0.045502           5     10000           clone
  3.25    0.001736           0     90329       322 read
  2.12    0.001133           0     20009           open
  2.03    0.001086           0     50001           dup2

pipe上記の統計を見ると、より多くの子プロセス(システムコール)が作成され、親プロセスが継続的に実行されるように、子cloneプロセス(システムコール)が完了するのを待つのに時間がかかることがわかります。wait4

Process substitutionいいえ。子プロセスから直接読み取ることができます。Process substitutionパラメータと変数の拡張と同時に実行されるコマンドはProcess Substitutionバックグラウンドで実行されます。からbash manpage

Process Substitution
       Process  substitution  is supported on systems that support named pipes
       (FIFOs) or the /dev/fd method of naming open files.  It takes the  form
       of  <(list) or >(list).  The process list is run with its input or out‐
       put connected to a FIFO or some file in /dev/fd.  The name of this file
       is  passed  as  an argument to the current command as the result of the
       expansion.  If the >(list) form is used, writing to the file will  pro‐
       vide  input  for list.  If the <(list) form is used, the file passed as
       an argument should be read to obtain the output of list.

       When available, process substitution is performed  simultaneously  with
       parameter  and variable expansion, command substitution, and arithmetic
       expansion.

修正する

子プロセスの統計に対して strace を実行します。

そしてpipe

$ strace -fqc ./pipe.sh 
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 70.76    0.215739           7     30000     10000 wait4
 28.04    0.085490           4     20000           clone
  0.78    0.002374           0    220008           rt_sigprocmask
  0.17    0.000516           0    110009     20000 close
  0.15    0.000456           0     10000           pipe

そしてproc-sub

$ strace -fqc ./procsub.sh 
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 52.38    0.033977           3     10000           clone
 32.24    0.020913           0     96070      6063 read
  5.24    0.003398           0     20009           open
  2.34    0.001521           0    110003     10001 fcntl
  1.87    0.001210           0    100009           close

おすすめ記事