「エコ」が「タッチ」よりはるかに速いのはなぜですか?

「エコ」が「タッチ」よりはるかに速いのはなぜですか?

ディレクトリ内のすべてのxmlファイルのタイムスタンプを現在の時刻に(再帰的に)更新しようとしています。私はMac OSX 10.8.5を使用しています。

約300,000個のファイルに対して、次のechoコマンドには次のものが必要です。10秒:

for file in `find . -name "*.xml"`; do echo >> $file; done

ただし、次のtouchコマンドには次のものが必要です。10分! :

for file in `find . -name "*.xml"`; do touch $file; done

エコーがタッチよりもはるかに速いのはなぜですか?

答え1

Bashではtouch外部バイナリechoですが、内蔵ケース:

$ type echo
echo is a shell builtin
$ type touch
touch is /usr/bin/touch

touch外部バイナリであり、ファイルごとに一度呼び出されるので、シェルからtouch300,000のインスタンスを作成する必要があります。touchこれは長い時間がかかります。

echoしかし、これはシェル組み込み関数なので、シェル組み込み関数の実行にはまったくフォークは必要ありません。代わりに、現在のシェルはすべての操作を実行し、外部プロセスを作成しないため、非常に高速です。

以下は、シェル操作の2つの概要です。を使用すると、新しいプロセスをcloneするのに時間がかかることがわかりますtouch/bin/echo組み込みの代わりにシェルを使用すると、同様の結果が表示されます。


タッチを使う

$ strace -c -- bash -c 'for file in a{1..10000}; do touch "$file"; done'
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 56.20    0.030925           2     20000     10000 wait4
 38.12    0.020972           2     10000           clone
  4.67    0.002569           0     80006           rt_sigprocmask
  0.71    0.000388           0     20008           rt_sigaction
  0.27    0.000150           0     10000           rt_sigreturn
[...]

エコを使う

$ strace -c -- bash -c 'for file in b{1..10000}; do echo >> "$file"; done'
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 34.32    0.000685           0     50000           fcntl
 22.14    0.000442           0     10000           write
 19.59    0.000391           0     10011           open
 14.58    0.000291           0     20000           dup2
  8.37    0.000167           0     20013           close
[...]

答え2

他の人が答えたように、コマンドを使用する方が通常(必須ではありませんが)シェルに組み込まれているよりも高速ですecho。これにより、ファイル実行ごとに新しいプロセスを開始することに関連するカーネルオーバーヘッドが削除されます。touchechotouch

ただし、この効果を得るための最速の方法はまだ使用することですが、touch各ファイルに対してプログラムを一度実行するのではなく、オプションを使用して数回実行することができ-execますfind。このアプローチはシェルループに関連するオーバーヘッドを防ぐので、通常は高速です。

find . -name "*.xml" -exec touch {} +

可能であれば+(代わりに\;)withを使用してfind ... -exec各ファイルを引数として使用して、コマンドを一度だけ実行します。パラメータリストが非常に長い場合(たとえば、300,000ファイルの場合)、制限にARG_MAX近いパラメータリストを使用して複数回実行されます(ほとんどのシステムでは)。

このアプローチのもう1つの利点は、元のループの場合ではなく、すべての空白文字を含むファイル名に強力であることです。

答え3

echoシェルが組み込まれています。一方、touch外部バイナリがあります。

$ type echo
echo is a shell builtin
$ type touch
touch is hashed (/usr/bin/touch)

シェル組み込み関数forkプログラムのロード時にオーバーヘッドが発生しないため、はるかに高速ですexec。したがって、組み込みコマンドと外部コマンドを複数回実行すると、かなりの時間差が観察されます。

timeこれがまさにこのようなユーティリティがシェル組み込みとして提供される理由です。

以下を使用して、シェル組み込みコマンドの完全なリストを取得できます。

enable -p

上記のように便利そして組み込みその結果、重大な性能低下が発生する。以下は、約9000個のファイルを生成するのにかかる時間の統計です。組み込み echoそして便利 echo:

# Using builtin
$ time bash -c 'for i in {1000..9999}; do echo > $i; done'

real    0m0.283s
user    0m0.100s
sys 0m0.184s

# Using utility /bin/echo
$ time bash -c 'for i in {1000..9999}; do /bin/echo > $i; done'

real    0m8.683s
user    0m0.360s
sys 0m1.428s

関連情報