ทำไมถึงเจอ - ประเภท f` ใช้เวลานานกว่า `หา '


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อาจเป็นรูปแบบที่ไม่ดี แต่ดูเหมือนว่าวิธีที่ตรงที่สุดในการใช้ตัวกรอง regex เมื่อตะกี้

คำสั่งต่อไปนี้มีเฉพาะไฟล์ในผลลัพธ์การค้นหาและใช้เวลานานขึ้นอย่างเห็นได้ชัด

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

ฉันเขียนโค้ดเพื่อทดสอบประสิทธิภาพของคำสั่งทั้งสองนี้ (ด้วยdashและtcshเพื่อแยกแยะผลกระทบใด ๆ ที่เชลล์อาจมีแม้ว่าจะไม่ควรมี) tcshผลที่ได้รับการละเว้นเพราะพวกเขากำลังหลักเดียวกัน

ผลลัพธ์ที่ฉันได้รับแสดงให้เห็นถึงโทษประสิทธิภาพ 10% -type f

นี่คือผลลัพธ์ของโปรแกรมที่แสดงจำนวนเวลาที่ใช้ในการดำเนินการซ้ำ 1,000 คำสั่งต่างๆ

% 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

ค้นหา busybox, นำไปใช้กับไดเรกทอรีสุ่มที่มี 4,3k dirs และไฟล์ 2,8k ทำงานในเวลาเดียวกันโดยมี-type fและไม่มี แต่ในตอนแรกเคอร์เนล Linux โหลดลงในแคชและการค้นพบครั้งแรกช้ากว่า

1
การเดาครั้งแรกของฉันคือ-type fตัวเลือกที่เกิดจากfindการโทรstat()หรือfstat()อะไรก็ตามเพื่อดูว่าชื่อไฟล์ตรงกับไฟล์ไดเรกทอรี symlink ฯลฯ ฯลฯ ฉันทำstraceตามfind . และ a find . -type fและร่องรอยก็เกือบจะเหมือนกัน ต่างกันในการwrite()โทรที่มีชื่อไดเรกทอรีอยู่เท่านั้น ดังนั้นฉันไม่รู้ แต่ฉันอยากรู้คำตอบ
Bruce Ediger

1
ไม่ใช่คำตอบสำหรับคำถามของคุณ แต่มีtimeคำสั่ง builtin เพื่อดูว่าคำสั่งใช้เวลาในการประมวลผลนานแค่ไหนคุณไม่จำเป็นต้องเขียนสคริปต์ที่กำหนดเองเพื่อทดสอบ
Elronnd

คำตอบ:


16

GNU find มีการเพิ่มประสิทธิภาพที่สามารถนำไปใช้ได้find .แต่ไม่ใช่find . -type f : หากรู้ว่าไม่มีรายการที่เหลืออยู่ในไดเรกทอรีใด ๆ ที่เป็นไดเรกทอรีมันจะไม่รบกวนการกำหนดประเภทของไฟล์ (ด้วยการstatเรียกของระบบ) เว้นแต่ว่าหนึ่งในนั้น เกณฑ์การค้นหาต้องการมัน การเรียกstatใช้เวลาที่สามารถวัดได้เนื่องจากข้อมูลโดยทั่วไปจะอยู่ในไอโหนดในตำแหน่งที่แยกต่างหากบนดิสก์แทนที่จะอยู่ในไดเรกทอรีที่มี

มันรู้ได้อย่างไร? เนื่องจากจำนวนลิงก์ในไดเรกทอรีระบุจำนวนไดเรกทอรีย่อยที่มี บนระบบไฟล์ Unix ทั่วไปจำนวนลิงก์ของไดเร็กทอรีคือ 2 บวกจำนวนไดเร็กทอรี: หนึ่งรายการสำหรับรายการไดเร็กทอรีในพาเรนต์หนึ่งรายการสำหรับ.รายการรายการและอีกหนึ่งสำหรับ..รายการในแต่ละไดเร็กทอรีย่อย

-noleafตัวเลือกที่บอกfindจะไม่ใช้การเพิ่มประสิทธิภาพนี้ สิ่งนี้มีประโยชน์หากfindมีการเรียกใช้บนระบบไฟล์บางระบบที่จำนวนไดเรกทอรีลิงก์ไม่เป็นไปตามอนุสัญญา Unix


สิ่งนี้ยังคงเกี่ยวข้องหรือไม่? เมื่อดูที่findแหล่งที่มามันจะใช้fts_open()และfts_read()โทรในปัจจุบัน
RealSkeptic

@RealSkeptic สิ่งนี้มีการเปลี่ยนแปลงในรุ่นล่าสุดหรือไม่? ฉันไม่ได้ตรวจสอบแหล่งที่มา แต่ทดลอง 4.4.2 ในเดเบียนเสถียรจะเพิ่มประสิทธิภาพการstatโทรเมื่อมันไม่ต้องการเนื่องจากการนับลิงก์ไดเรกทอรีและ-noleafตัวเลือกที่มีเอกสารในคู่มือ
Gilles 'หยุดชั่วร้าย'

มันปรับให้เหมาะสมstatแม้ในfts...รุ่น - มันผ่านการตั้งค่าสถานะที่เหมาะสมสำหรับการfts_openโทร แต่สิ่งที่ฉันไม่แน่ใจว่ายังเกี่ยวข้องคือการตรวจสอบกับจำนวนลิงก์ มันจะตรวจสอบแทนว่าเร็กคอร์ด fts ที่ส่งคืนมีหนึ่งในแฟล็ก "directory" หรือไม่ อาจเป็นได้ว่าfts_readตัวเองตรวจสอบลิงก์เพื่อตั้งค่าสถานะนั้น แต่findไม่ได้ คุณสามารถดูได้ว่าเวอร์ชั่นของคุณขึ้นอยู่กับการftsโทรfind --versionหรือไม่
RealSkeptic

@Gilles ในfindทางทฤษฎีจะสามารถกำหนดได้ว่ารายการทั้งหมดในไดเรกทอรีเป็นไดเรกทอรีด้วยและใช้ข้อมูลนั้นเมื่อใด
Gregory Nisbet

@GregoryNisbet ตามทฤษฎีแล้วใช่ แต่ซอร์สโค้ด (ตอนนี้ฉันตรวจสอบแล้ว) ไม่ได้ทำอย่างนั้นน่าจะเป็นเพราะมันเป็นกรณีที่หายากกว่ามาก
Gilles 'หยุดชั่วร้าย'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.