Чому `знаходить. -тип f` займає більше часу, ніж "find."


15

Схоже 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 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);
    }
}

2
Схоже find, доведеться перевірити, чи відповідає той чи інший шлях файлу чи директорії, щоб рекурсивно проходити вміст каталогів. - доведеться перевірити, чи це каталог, не доведеться перевіряти, чи це файл. Є й інші типи запису: іменовані труби, символічні посилання, блокування спеціальних пристроїв, розетки ... Тож, хоча, можливо, це було зроблено перевірки, щоб побачити, чи це каталог, це не означає, що він знає, чи це звичайний файл.
RealSkeptic

Знайти зайнятий файл, застосований до випадкової директорії з 4,3k dirs та 2,8k файлами, що працюють одночасно з -type fі без нього. Але спочатку ядро ​​Linux завантажило його в кеш, і найперше пошук виявився повільніше.

1
Моя перша здогадка полягала в тому, що -type fопція викликала findдзвінок stat()або що- fstat()небудь для того, щоб дізнатись, чи відповідає ім'я файлу файлу, директорії, симпосилання тощо. Я зробив записи stracea find . і a, find . -type fі слід був майже однаковий, відрізняються лише write()дзвінками, які мали в них імена каталогів. Отже, я не знаю, але хочу знати відповідь.
Брюс Едігер

1
Насправді не відповідь на ваше запитання, але є timeвбудована команда, щоб побачити, скільки часу потрібно виконувати команді, вам не потрібно було писати спеціальний сценарій для тестування.
Елронд

Відповіді:


16

Знайти GNU має оптимізацію, до якої можна застосувати, find .але не find . -type f: якщо вона знає, що жодна з решти записів у каталозі не є каталогами, то визначати тип файлу (із statсистемним викликом) не турбується, якщо один із критерії пошуку цього вимагають. Виклик statможе зайняти вимірюваний час, оскільки інформація, як правило, знаходиться в inode, в окремому місці на диску, а не в каталозі, що містить.

Звідки це знати? Оскільки кількість посилань у каталозі вказує, скільки у них є підкаталогів. У типових файлових системах Unix кількість посилань каталогів становить 2 плюс кількість каталогів: один для запису каталогу в його батьківському, один для .запису та один для ..запису у кожному підкаталозі.

Цей -noleafпараметр повідомляє findне застосовувати цю оптимізацію. Це корисно, якщо findйого викликають у деякій файловій системі, де кількість посилань каталогів не відповідає конвенції Unix.


Це все ще доречно? Дивлячись на findджерело, воно просто використовує fts_open()і fts_read()зараз телефонує.
RealSkeptic

@RealSkeptic Чи змінилося це в останніх версіях? Я не перевіряв джерело, але експериментально, версія 4.4.2 в стабільній Debian оптимізує statвиклики, коли вони не потребують їх через кількість посилань каталогів, а -noleafпараметр задокументовано в посібнику.
Жил "ТАК - перестань бути злим"

Він оптимізується statнавіть у fts...версії - він передає відповідний прапор для цього на fts_openвиклик. Але те, що я не впевнений, все ще актуальне, - це перевірка кількості посилань. Замість цього він перевіряє, чи є у поверненому записі fts один із прапорів "каталогу". Можливо, він fts_readсам перевіряє посилання для встановлення цього прапора, але findне робить. Ви можете бачити, чи покладається ваша версія fts, зателефонувавши find --version.
RealSkeptic

@ Gilles, Чи findтеоретично вдалося б визначити, коли всі записи в каталозі теж каталоги, і використовувати цю інформацію?
Григорій Нісбет

@GregoryNisbet Теоретично так, але вихідний код (я зараз перевірив) цього не робить, імовірно, тому що це набагато рідше.
Жил "ТАК - перестань бути злим"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.