「エコー」が「タッチ」よりもずっと速いのはなぜですか?


116

ディレクトリ内のすべての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

エコーがここに触れるよりもずっと速いのはなぜですか?


20
ちょうど側の発言:あなたはないこれら2つのコマンドは、あなたが、等価ではないんではないことを知っていますか?少なくともUnix / Linuxでは、echo >> $file改行を追加して$file変更します。OS / Xでも同じになると思います。そうしたくない場合は、を使用してくださいecho -n >> $file
ドゥブ14

2
またtouch `find . -name "*.xml"` 、上記の両方よりも高速ではないでしょうか?
エルモ14

4
または考慮してください>>$file
gerrit 14

8
明示的な質問への答えではありませんが、なぜtouchそんなに何度も呼び出すのですか?はるかに少ない回数(おそらく1回)をfind . -name '*.xml' -print0 | xargs -0 touch呼び出しますtouch。Linux上での動作は、OS X上で動作するはずです
マイク・レンフロ

3
@elmo引数リストが長すぎる(簡単に、300.000ファイルで...)
Rmano

回答:


161

bashではtouch、外部バイナリechoですが、組み込みシェルです:

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

以来touch、外部バイナリで、あなたが起動しtouch、ファイルごとに一度、シェル30万個のインスタンスの作成する必要がありtouch、長い時間がかかります。

echoただし、シェル組み込みコマンドであり、シェル組み込みコマンドの実行にはフォークはまったく必要ありません。代わりに、現在のシェルがすべての操作を実行し、外部プロセスは作成されません。これが非常に速い理由です。

シェルの操作の2つのプロファイルを以下に示します。を使用すると、新しいプロセスのクローン作成に多くの時間が費やされることがわかり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
[...]

1
OS Xでstraceをコンパイルしたか、別のOSでテストを実行しましたか?
bmike 14

1
@bmike私のテストはLinuxで行われますが、原理は同じです。
クリスダウン14

私は完全に同意します-/ bin / echoが/ bin / touchと同じくらい遅いので、推論が正しいという主な質問に関する私のコメントを参照してください。straceのタイミングを再現したかっただけで、dtruss / dtraceを使用すると失敗し、bash -c構文はOS Xでも期待どおりに機能しません。
bmike 14

71

他の人が答えたように、使用するとechoより速くなりますtouchようecho、組み込みのシェルに共通しているコマンド(である必要はないが)です。これを使用すると、取得するファイルごとに新しいプロセスを開始することに関連するカーネルのオーバーヘッドがなくなりますtouch

ただし、この効果を実現する最速の方法はを使用するtouchことですが、ファイルごとにプログラムを1 回実行するのではなく、-execwith findを使用して、数回だけ実行されるようにすることができます。このアプローチは、シェルループに関連するオーバーヘッドを回避するため、通常は高速になります。

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

+(とは対照的に\;)with を使用すると、find ... -exec可能であれば、各ファイルを引数としてコマンドを1回だけ実行します。引数リストが非常に長い場合(300,000ファイルの場合のように)、制限に近い長さを持つ引数リストを使用して複数の実行が行われます(ARG_MAXほとんどのシステムで)。

このアプローチの別の利点は、すべての空白文字を含むファイル名で堅牢に動作することです。これは、元のループには当てはまりません。


17
+1検索指摘して+議論を。多くの人がこれに気づいていないと思います(私は知りませんでした)。
ヘリット・

7
のすべてのバージョンに引数findがあるわけではありません+。にパイプすることで同様の効果を得ることができますxargs
バーマー14

5
@Barmar、この+部分はPOSIXで必要とされるため、移植性が必要です。-print0そうではありません。
グレアム14

1
私はまだそれを持っていない実装に時々出くわします。YMMV。
バーマー14

1
@ChrisDown、私が発見したことは、Busyboxにfindは利用可能なオプションがありますが、それを;表面の下のように扱うだけです。
グレアム14

29

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

そして、私はそこだと思うechoほとんどのシステム上のバイナリが(私にとって、それはだ/bin/echo)ので、あなたはその代わりに、ビルトインの使用タイミングテストを再試行することができます
マイケルMrozek

@MichaelMrozek組み込みおよびバイナリのタイミングテストを追加しました。
devnull
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.