Кажется, find
что в любом случае придется проверить, соответствует ли данный путь файлу или каталогу, чтобы рекурсивно просмотреть содержимое каталогов.
Вот некоторая мотивация и то, что я сделал на местном уровне, чтобы убедить себя, что на find . -type f
самом деле медленнее, чем find .
. Я еще не копался в GNU найти исходный код.
Поэтому я создаю резервные копии некоторых файлов в моем $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
Я написал некоторый код для проверки производительности этих двух команд (с 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);
}
}
источник
find
что в любом случае придется проверить, соответствует ли данный путь файлу или каталогу, чтобы рекурсивно просмотреть содержимое каталогов. - он должен проверить, является ли он каталогом, он не должен проверять, является ли он файлом. Существуют и другие типы записей: именованные каналы, символические ссылки, блочные специальные устройства, сокеты ... Так что, возможно, он уже выполнил проверку, чтобы определить, является ли это каталогом, но это не значит, что он знает, является ли он обычным файлом.-type f
и без него. Но вначале ядро Linux загрузило его в кеш, и самая первая находка была медленнее.-type f
параметр вызвалfind
вызовstat()
илиfstat()
что-то еще, чтобы выяснить, соответствует ли имя файла файлу, каталогу, символической ссылке и т. Д. И т. Д. Я сделалstrace
onfind .
и a,find . -type f
и трассировка была почти идентична, отличается толькоwrite()
вызовами, в которых были имена каталогов. Итак, я не знаю, но я хочу знать ответ.time
встроенная команда, чтобы увидеть, сколько времени занимает выполнение команды, вам не нужно было писать собственный скрипт для тестирования.Ответы:
GNU find имеет оптимизацию, которую можно применять,
find .
но не к нейfind . -type f
: если он знает, что ни одна из оставшихся записей в каталоге не является каталогами, он не будет определять тип файла (с помощьюstat
системного вызова), если только один из критерии поиска требуют этого. Вызовstat
может занять измеримое время, так как информация обычно находится в inode, в отдельном месте на диске, а не в директории, содержащей ее.Как это узнать? Потому что количество ссылок на каталог указывает, сколько у него подкаталогов. В типичных файловых системах Unix количество ссылок на каталог равно 2 плюс количество каталогов: один для записи каталога в его родительском элементе, один для
.
записи и один для..
записи в каждом подкаталоге.-noleaf
Опция говоритfind
не применять эту оптимизацию. Это полезно, еслиfind
вызывается в некоторой файловой системе, где количество ссылок на каталоги не соответствует соглашению Unix.источник
find
источник, он просто используетfts_open()
иfts_read()
вызывает в настоящее время.stat
вызовы, когда они не нужны, из-за количества ссылок в каталогах, и эта-noleaf
опция описана в руководстве.stat
даже вfts...
версии - он передает соответствующий флаг дляfts_open
вызова. Но то, что я не уверен, все еще уместно - проверка с количеством ссылок. Вместо этого он проверяет, имеет ли возвращенная запись fts один из флагов «каталога». Может быть, онfts_read
сам проверяет ссылки, чтобы установить этот флаг, ноfind
не делает этого. Вы можете увидеть, зависит ли ваша версияfts
, позвонивfind --version
.find
теоретически сможет определить, когда все все записи в каталоге тоже каталоги, и использовать эту информацию?