find 始终按字典顺序列出文件,因为这就是它执行深度优先搜索的方式。如果我们愿意放宽该限制,是否可以提高 find 的并行性或使用另一个类似的工具来查找空文件? (我也对使用其他类似标准查找文件的策略感到好奇find
,但为了具体起见,让我们使用空文件)。
因此,我试图以任意顺序查找主目录中的所有空文件。
在 OS X 上使用 bash,我运行以下命令
$ find . -type f -empty >& /dev/null
real 0m10.334s
user 0m0.525s
sys 0m5.568s
为了提高并行性,我做了我能想到的最简单的事情,并使用 Perl 脚本对每个目录调用一次查找。 Perl 脚本仅find
在其自己的进程中的每个顶级目录或文件中运行。
该脚本的总运行时间略低于单个查找的一半。
#!/usr/bin/env perl
use strict;
use warnings;
opendir(my $fh, '.');
while (readdir($fh)) {
my $item = $_;
next if $item eq '.';
next if $item eq '..';
my $cpid = fork();
if ($cpid == -1) {
die;
} elsif ($cpid == 0) {
exec 'find', "./$item", '-type', 'f', '-empty', or die;
}
}
while (wait() != -1) {}
例如
$ time perl find-parallel.pl >& /dev/null
real 0m4.245s
user 0m1.126s
sys 0m8.281s
find
使用某种脚本在一定深度手动运行独立似乎是解决这个问题的一种相当笨拙的方法。有没有更好的办法?
答案1
首先是小问题:find 的输出顺序不是按字母顺序排列的,至少在 Linux 上是这样。相反,它按照目录索引顺序(通常是创建顺序)。
exec
其本身(直到 syscall execve
)在您正在工作的规模上具有不小的开销,因此您需要避免它。
作为解决方案的通用框架,您至少需要两个线程的基础:
- 队列管理器
- 工人)
逻辑:
- 队列以单个目录开始
.
。 - 每当队列中有可用的东西,并且我们尚未达到并行线程的限制时,就用队列中的一项启动一个工作线程。
- 工作人员:非递归地读取给定目录。
- 对于它看到的新目录,将目录追加到队列中。
- 对于其他新文件,正常处理。
特殊情况需要处理:
- 指向同一事物的多个符号链接。
- 到其他目录的符号链接(根据您的设计需要,您可能不会遵循,或者必须遵循多次)。
- 如果跟随符号链接到更高级别,则循环。
这会比非并行查找性能更好吗?这是一个困难的问题,并且还取决于所使用的文件系统/内核。
例如,如果您正在寻找预构建的解决方案,请寻找 Go 并行目录遍历器,但要注意以下成本:额外stat
通话。
答案2
这是 Github 上一个免费的 Linux 并行查找工具,是我用 C++ 编写的: https://github.com/breuner/elfindo
您可以像这样使用它来查找空文件:
$ elfindo . -type f -size 0
您可以通过参数显式设置线程数-threads
:
$ elfindo . -type f -size 0 -threads 32