Tại sao `tìm. -type f` mất nhiều thời gian hơn `find .`?


15

Dường như findsẽ phải kiểm tra xem một đường dẫn cụ thể có tương ứng với một tệp hoặc thư mục hay không để đi theo cách đệ quy nội dung của các thư mục.

Đây là một số động lực và những gì tôi đã làm tại địa phương để thuyết phục bản thân rằng find . -type fthực sự chậm hơn find .. Tôi chưa đi sâu vào GNU tìm mã nguồn.

Vì vậy, tôi đang sao lưu một số tệp trong $HOME/Workspacethư mục của mình và loại trừ các tệp phụ thuộc vào các dự án hoặc tệp kiểm soát phiên bản của tôi.

Vì vậy, tôi đã chạy lệnh sau đó thực hiện nhanh chóng

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

findĐường ống grepcó thể là hình thức xấu, nhưng có vẻ như là cách trực tiếp nhất để sử dụng bộ lọc regex phủ định.

Lệnh sau chỉ bao gồm các tệp trong đầu ra của find và mất nhiều thời gian hơn.

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

Tôi đã viết một số mã để kiểm tra hiệu năng của hai lệnh này (với dashtcsh, chỉ để loại trừ bất kỳ hiệu ứng nào mà trình bao có thể có, mặc dù không nên có bất kỳ). Các tcshkết quả đã bị bỏ qua vì về cơ bản chúng giống nhau.

Kết quả tôi nhận được cho thấy mức phạt hiệu suất 10% cho -type f

Đây là đầu ra của chương trình hiển thị lượng thời gian cần thiết để thực hiện 1000 lần lặp của các lệnh khác nhau.

% 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

Đã thử nghiệm với

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

Trên Ubuntu 15.10

Đây là tập lệnh perl tôi đã sử dụng để đo điểm chuẩn

#!/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
Dường như findsẽ phải kiểm tra xem một đường dẫn cụ thể có tương ứng với một tệp hoặc thư mục hay không để đi theo cách đệ quy nội dung của các thư mục. - nó sẽ phải kiểm tra nếu nó là một thư mục, nó sẽ không phải kiểm tra xem nó có phải là một tập tin hay không. Có các loại mục nhập khác: ống có tên, liên kết tượng trưng, ​​chặn các thiết bị đặc biệt, ổ cắm ... Vì vậy, mặc dù có thể đã thực hiện kiểm tra để xem đó có phải là một thư mục hay không, điều đó không có nghĩa là nó có phải là một tệp thông thường hay không.
RealSkeptic

busybox find, áp dụng cho thư mục ngẫu nhiên với 4,3k dir và 2,8k tệp chạy cùng lúc với -type fvà không có nó. Nhưng lần đầu tiên, nhân Linux đã tải nó vào bộ đệm và lần đầu tiên tìm thấy chậm hơn.

1
Dự đoán đầu tiên của tôi là -type ftùy chọn gây ra findđể gọi stat()hoặc fstat()bất cứ điều gì để tìm hiểu xem tên tệp có tương ứng với một tệp, thư mục, symlink, v.v. Tôi đã thực hiện stracemột find . và một find . -type fvà dấu vết gần như giống hệt nhau, chỉ khác nhau trong các write()cuộc gọi có tên thư mục trong đó. Vì vậy, tôi không biết, nhưng tôi muốn biết câu trả lời.
Bruce Ediger

1
Không thực sự là một câu trả lời cho câu hỏi của bạn, nhưng có một timelệnh dựng sẵn để xem một lệnh mất bao lâu để thực thi, bạn không thực sự cần phải viết một tập lệnh tùy chỉnh để kiểm tra.
Elronnd

Câu trả lời:


16

GNU find có một tối ưu hóa có thể được áp dụng find .nhưng không phải find . -type f: nếu nó biết rằng không có mục nào còn lại trong thư mục là thư mục, thì nó không cần xác định loại tệp (với lệnh statgọi hệ thống) trừ khi một trong các tiêu chí tìm kiếm đòi hỏi nó. Việc gọi statcó thể mất thời gian có thể đo được vì thông tin thường ở trong nút, ở một vị trí riêng trên đĩa, thay vì trong thư mục chứa.

Làm sao nó biết? Bởi vì số lượng liên kết trên một thư mục cho biết nó có bao nhiêu thư mục con. Trên các hệ thống tệp Unix điển hình, số lượng liên kết của một thư mục là 2 cộng với số lượng thư mục: một cho mục nhập của thư mục trong cha mẹ của nó, một cho .mục nhập và một cho ..mục nhập trong mỗi thư mục con.

Các -noleaftùy chọn nói findkhông áp dụng tối ưu hóa này. Điều này hữu ích nếu findđược gọi trên một số hệ thống tập tin trong đó số lượng liên kết thư mục không tuân theo quy ước Unix.


Đây có còn là thích hợp? Nhìn vào findnguồn, nó chỉ đơn giản là sử dụng fts_open()fts_read()gọi ngày nay.
RealSkeptic

@RealSkeptic Điều này có thay đổi trong các phiên bản gần đây không? Tôi chưa kiểm tra nguồn, nhưng về mặt thực nghiệm, phiên bản 4.4.2 trong ổn định Debian không tối ưu hóa statcác cuộc gọi khi không cần đến chúng do số lượng liên kết thư mục và -noleaftùy chọn này được ghi lại trong hướng dẫn.
Gilles 'SO- ngừng trở nên xấu xa'

Nó tối ưu hóa statngay cả trong fts...phiên bản - nó chuyển cờ thích hợp cho fts_opencuộc gọi đó. Nhưng điều tôi không chắc vẫn còn thích hợp là kiểm tra với số lượng liên kết. Thay vào đó, nó kiểm tra xem bản ghi fts trả về có một trong các cờ "thư mục" hay không. Có thể là fts_readchính nó kiểm tra các liên kết để đặt cờ đó, nhưng findkhông. Bạn có thể xem liệu phiên bản của bạn dựa vào ftsbằng cách gọi find --version.
RealSkeptic

@Gilles, findvề mặt lý thuyết sẽ có thể xác định khi tất cả các mục trong một thư mục cũng là thư mục và sử dụng thông tin đó?
Gregory Nisbet

@GregoryNisbet Về lý thuyết thì có, nhưng mã nguồn (hiện tại tôi đã kiểm tra) không làm điều đó, có lẽ vì đó là trường hợp hiếm hơn nhiều.
Gilles 'SO- ngừng trở nên xấu xa'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.