私のディレクトリにあるすべての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
エコーがここで触れるよりもはるかに速いのはなぜですか?
Bashでは、touch
は外部バイナリですが、echo
は Shell builtin :
$ type echo
echo is a Shell builtin
$ type touch
touch is /usr/bin/touch
touch
は外部バイナリであり、ファイルごとにtouch
を呼び出すため、シェルはtouch
のインスタンスを300,000作成する必要があり、これには長い時間がかかります。
ただし、echo
はShellビルトインであり、Shellビルトインの実行にはforkはまったく必要ありません。代わりに、現在のシェルがすべての操作を実行し、外部プロセスは作成されません。これが非常に高速である理由です。
シェルの操作の2つのプロファイルを次に示します。 touch
を使用すると、新しいプロセスの複製に多くの時間が費やされていることがわかります。 Shellビルトインの代わりに/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
[...]
他の人が答えたように、echo
は一般に(必須ではありませんが)シェルに組み込まれているコマンドであるため、touch
を使用するとecho
よりも高速になります。これを使用すると、touch
で取得する各ファイルに対して新しいプロセスの開始を実行することに関連するカーネルオーバーヘッドが不要になります。
ただし、この効果を実現する最も速い方法は、touch
を使用することですが、ファイルごとに1回プログラムを実行するのではなく、find
で-exec
オプションを使用して、それは数回だけ実行されます。このアプローチは、シェルループに関連するオーバーヘッドを回避するため、通常は高速になります。
find . -name "*.xml" -exec touch {} +
+
で\;
を(find ... -exec
とは対照的に)使用すると、各ファイルを引数として可能な場合はコマンドが1回だけ実行されます。引数リストが非常に長い場合(300,000ファイルの場合のように)、制限(ほとんどのシステムではARG_MAX
)に近い長さの引数リストで複数の実行が行われます。
このアプローチの別の利点は、元のループの場合とは異なり、すべての空白文字を含むファイル名で確実に動作することです。
echo
は組み込みのシェルです。一方、touch
は外部バイナリです。
$ type echo
echo is a Shell builtin
$ type touch
touch is hashed (/usr/bin/touch)
Shell builtins は、プログラムのロードに伴うオーバーヘッドがないため、つまりfork
/exec
が関与しないため、はるかに高速です。そのため、組み込みコマンドと外部コマンドを何度も実行すると、大きな時間差が見られます。
これが、time
のようなユーティリティがシェル組み込みとして使用できる理由です。
Shellビルトインの完全なリストは、次のようにして取得できます。
enable -p
上記のように、utilityをbuiltinではなく使用すると、パフォーマンスが大幅に低下します。以下は、builtinecho
およびutilityecho
を使用して〜9000ファイルを作成するのにかかる時間の統計です。
# 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