なぜ `find。-type f`は `find .`よりも時間がかかりますか?


15

findディレクトリの内容を再帰的にたどるには、とにかく、指定されたパスがファイルまたはディレクトリに対応するかどうかを確認する必要があるようです。

ここにいくつかの動機と、find . -type f実際にが遅いことを自分に納得させるためにローカルで行ったことがありますfind .。GNU findのソースコードをまだ掘り下げていません。

そのため、$HOME/Workspaceディレクトリ内のいくつかのファイルをバックアップし、プロジェクトの依存関係またはバージョン管理ファイルのいずれかであるファイルを除外しています。

だから私はすぐに実行される次のコマンドを実行しました

% find Workspace/ | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > ws-files-and-dirs.txt

findパイプ処理は不適切grepな形式かもしれませんが、否定正規表現フィルターを使用する最も直接的な方法のように思えました。

次のコマンドでは、findの出力にファイルのみが含まれ、著しく時間がかかりました。

% find Workspace/ -type f | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > ws-files-only.txt

これらの2つのコマンドのパフォーマンスをテストするためのコードをいくつか作成しました(とdashを使用してtcsh、シェルが存在するはずのない影響を除外するためだけに)。tcsh彼らは本質的に同じだから結果が省略されています。

私が得た結果は、約10%のパフォーマンスペナルティを示しました。 -type f

これは、さまざまなコマンドの1000回の反復の実行にかかった時間を示すプログラムの出力です。

% perl tester.pl
/bin/sh -c find Workspace/ >/dev/null
82.986582

/bin/sh -c find Workspace/ | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > /dev/null
90.313318

/bin/sh -c find Workspace/ -type f >/dev/null
102.882118

/bin/sh -c find Workspace/ -type f | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > /dev/null

109.872865

でテスト済み

% find --version
find (GNU findutils) 4.4.2
Copyright (C) 2007 Free Software Foundation, Inc.

Ubuntu 15.10で

これが、ベンチマークに使用したperlスクリプトです。

#!/usr/bin/env perl
use strict;
use warnings;
use Time::HiRes qw[gettimeofday tv_interval];

my $max_iterations = 1000;

my $find_everything_no_grep = <<'EOF';
find Workspace/ >/dev/null
EOF

my $find_everything = <<'EOF';
find Workspace/ | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > /dev/null
EOF

my $find_just_file_no_grep = <<'EOF';
find Workspace/ -type f >/dev/null
EOF

my $find_just_file = <<'EOF';
find Workspace/ -type f | grep -v '/vendor\|/node_modules/\|Workspace/sources/\|/venv/\|/.git/' > /dev/null
EOF

my @finds = ($find_everything_no_grep, $find_everything,
    $find_just_file_no_grep, $find_just_file);

sub time_command {
    my @args = @_;
    my $start = [gettimeofday()];
    for my $x (1 .. $max_iterations) {
        system(@args);
    }
    return tv_interval($start);
}

for my $shell (["/bin/sh", '-c']) {
    for my $command (@finds) {
        print "@$shell $command";
        printf "%s\n\n", time_command(@$shell, $command);
    }
}

2
findディレクトリの内容を再帰的にたどるには、とにかく特定のパスがファイルまたはディレクトリに対応しているかどうかをチェックする必要があるようです。-ディレクトリかどうかを確認する必要があります。ファイルかどうかを確認する必要はありません。他のエントリタイプがあります:名前付きパイプ、シンボリックリンク、ブロック特殊デバイス、ソケット...ですから、ディレクトリであるかどうかを確認するために既にチェックを行っているかもしれませんが、それが通常のファイルであるかどうかを知っているわけではありません。
RealSkeptic

busybox findは、4,3k個のディレクトリと2,8k個のファイルを含むランダムディレクトリに適用され、ファイルの-type f有無にかかわらず同時に実行されます。しかし、最初はLinuxカーネルがそれをキャッシュにロードし、最初の検索は遅くなりました。

1
私の最初の推測では、ということでした-type f原因オプションfindのコールにstat()fstat()などなど、ファイル名はファイルに対応しているかどうかを確認するために、または何でも、ディレクトリ、シンボリックリンク、私がやったstracefind . find . -type f、トレースはほぼ同一でしたwrite()ディレクトリ名が含まれる呼び出しのみが異なります。だから、私は知りませんが、答えを知りたいです。
ブルースエディガー16年

1
質問に対する答えではありませんが、timeコマンドの実行にかかる時間を確認するための組み込みコマンドがあります。テストするためにカスタムスクリプトを記述する必要はありません。
エルロンド

回答:


16

GNU FINDはに適用することができ、最適化があるfind .が、ないようにするfind . -type f:それはディレクトリ内の残りのエントリのいずれもを知らない場合はディレクトリがあり、それは(とファイルタイプを決定するためにわざわざしないstatのいずれかの場合を除き、システムコール)検索条件にはそれが必要です。stat通常、情報は格納ディレクトリではなく、ディスク上の別の場所にあるiノードにあるため、呼び出しにはかなりの時間がかかる場合があります。

どうやってわかるの?ディレクトリのリンクカウントは、サブディレクトリの数を示しているためです。一般的なUnixファイルシステムでは、ディレクトリのリンクカウントは2にディレクトリの数を加えたものです。1つは親のディレクトリエントリ.用、もう1つは..エントリ用、各サブディレクトリのエントリ用です。

-noleafオプションが伝えfindこの最適化を適用しません。これはfind、ディレクトリリンクカウントがUnixの規則に従っていないファイルシステムで呼び出された場合に便利です。


これはまだ適切ですか?findソースを見ると、最近では単にとを使用しfts_open()ていfts_read()ます。
RealSkeptic

@RealSkepticこれは最近のバージョンで変更されましたか?ソースを確認していませんが、実験的には、Debian安定版のバージョン4.4.2はstatディレクトリリンクカウントのために呼び出しを必要としないときに呼び出しを最適化し、-noleafオプションはマニュアルに記載されています。
ジル「SO-悪であるのをやめる」

バージョンでstatも最適化されますfts...-そのための適切なフラグをfts_open呼び出しに渡します。しかし、まだ適切かどうかわからないのは、リンクの数をチェックすることです。代わりに、返されたftsレコードに「ディレクトリ」フラグの1つがあるかどうかをチェックします。これは、ことかもしれfts_read自体がそのフラグを設定するためのリンクをチェックしますが、findしません。をfts呼び出すことで、バージョンが依存しているかどうかを確認できますfind --version
RealSkeptic

@Gilles、find理論的には、ディレクトリ内のすべてのエントリがすべてディレクトリでもあるかどうかを判断し、その情報を使用できますか?
グレゴリーニズベット

@GregoryNisbet理論上はそうですが、ソースコード(私は今チェックしています)はそれを行いません。これはおそらく非常にまれなケースだからです。
ジル 'SO-悪であるのをやめる'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.