为什么`找到. -type f` 比 `find .` 花费更长的时间?

为什么`找到. -type f` 比 `find .` 花费更长的时间?

似乎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

我编写了一些代码来测试这两个命令的性能(使用dashtcsh,只是为了排除 shell 可能产生的任何影响,即使不应该有任何影响)。结果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);
    }
}

答案1

GNU find 有一个优化,可以应用于find .但不能应用于find . -type f:如果它知道目录中的剩余条目都不是目录,那么它不会费心确定文件类型(使用系统stat调用),除非其中之一搜索条件需要它。调用stat可能需要相当长的时间,因为信息通常位于磁盘上单独位置的 inode 中,而不是位于包含的目录中。

它是怎么知道的?因为目录上的链接计数表明它有多少个子目录。在典型的 Unix 文件系统上,目录的链接计数为 2 加上目录数:1 个用于其父目录中的目录条目,1 个用于条目.,1 个用于..每个子目录中的条目。

-noleaf选项指示find不应用此优化。如果find在目录链接计数不遵循 Unix 约定的某些文件系统上调用,这非常有用。

相关内容