Mengapa perintah “find | grep 'nama file' ”jauh lebih lambat daripada“ temukan 'nama file' ”?

10

Saya mencoba kedua perintah dan perintah find | grep 'filename' berkali-kali lebih lambat daripada find 'filename' perintah sederhana .

Apa penjelasan yang tepat untuk perilaku ini?

yoyo_fun
sumber
2
Anda membuat daftar setiap file dengan find dan kemudian meneruskan data ke grep untuk diproses. Dengan find yang digunakan sendiri, Anda kehilangan langkah melewati setiap file yang terdaftar untuk melakukan grep untuk mem-parsing output. Karena itu ini akan lebih cepat.
Raman Sailopal
Lebih lambat dalam arti apa? Apakah perintah membutuhkan waktu yang berbeda untuk diselesaikan?
Kusalananda
1
Saya tidak dapat mereproduksi ini secara lokal. Jika ada, time find "$HOME" -name '.profile'laporkan waktu yang lebih lama dari time find "$HOME" | grep -F '.profile'. (17 vs 12d).
Kusalananda
2
@JenniferAnderson Saya menjalankan keduanya berulang kali. 17 dan 12 detik adalah rata-rata. Dan ya, grepvariasi akan cocok di mana saja dalam findhasil, sedangkan pencocokan dengan find -namehanya akan sama persis (dalam hal ini).
Kusalananda
2
Ya, find filename pasti cepat . Saya agak berasumsi bahwa ini adalah kesalahan ketik dan yang dimaksud OP find -name filename. Dengan find filename, hanya filenameakan diperiksa (dan tidak ada yang lain).
Kusalananda

Jawaban:

11

(Saya menganggap GNU di findsini)

Menggunakan adil

find filename

akan cepat, karena itu hanya akan kembali filename, atau nama-nama di dalamnya filenamejika itu adalah direktori, atau kesalahan jika nama itu tidak ada di direktori saat ini. Ini adalah operasi yang sangat cepat, mirip dengan ls filename(tetapi rekursif jika filenamedirektori).

Sebaliknya,

find | grep filename

akan memungkinkan finduntuk menghasilkan daftar semua nama dari direktori saat ini dan di bawah, yang grepkemudian akan disaring. Ini jelas akan menjadi operasi yang jauh lebih lambat.

Saya berasumsi bahwa apa yang sebenarnya dimaksudkan itu

find . -type f -name 'filename'

Ini akan dicari filenamesebagai nama file biasa di mana saja di direktori saat ini atau di bawah.

Ini akan sama cepat (atau sebanding cepat) dengan find | grep filename, tetapi grepsolusinya akan cocok filenamedengan path lengkap dari setiap nama yang ditemukan, sama dengan apa yang -path '*filename*'akan dilakukan dengan find.


Kebingungan muncul dari kesalahpahaman tentang bagaimana cara findkerjanya.

Utilitas mengambil sejumlah jalur dan mengembalikan semua nama di bawah jalur ini.

Anda kemudian dapat membatasi nama yang dikembalikan menggunakan berbagai tes yang dapat bertindak pada nama file, path, timestamp, ukuran file, jenis file, dll.

Kapan kamu berkata

find a b c

Anda meminta finddaftar setiap nama yang tersedia di bawah tiga jalur a, bdan c. Jika ini adalah nama-nama file biasa di direktori saat ini, maka ini akan dikembalikan. Jika salah satu dari mereka kebetulan merupakan nama direktori, maka itu akan dikembalikan bersama dengan semua nama lebih lanjut di dalam direktori itu.

Kapan saya melakukannya

find . -type f -name 'filename'

Ini menghasilkan daftar semua nama di direktori saat ini ( .) dan di bawah. Kemudian itu membatasi nama-nama untuk orang-orang dari file biasa, yaitu bukan direktori dll, dengan -type f. Lalu ada batasan lebih lanjut untuk nama yang cocok filenamemenggunakan -name 'filename'. String filenamemungkin merupakan pola globbing nama file, seperti *.txt(ingatlah untuk mengutipnya!).

Contoh:

Tampaknya ini "menemukan" file yang dipanggil .profiledi direktori home saya:

$ pwd
/home/kk
$ find .profile
.profile

Tetapi pada kenyataannya, itu hanya mengembalikan semua nama di jalan .profile(hanya ada satu nama, dan itu dari file ini).

Lalu saya cdnaik satu level dan coba lagi:

$ cd ..
$ pwd
/home
$ find .profile
find: .profile: No such file or directory

The findperintah sekarang tidak dapat menemukan jalan yang disebut .profile.

Namun, jika saya mendapatkannya untuk melihat direktori saat ini, dan kemudian membatasi hanya nama yang dikembalikan.profile , ia juga menemukannya dari sana:

$ pwd
/home
$ find . -name '.profile'
./kk/.profile
Kusalananda
sumber
1
find filenameakan kembali hanya filenamejika filenamebukan dari direktori tipe (atau direktori tipe, tetapi tidak memiliki entri sendiri)
Stéphane Chazelas
2

Penjelasan Non-Teknis: Mencari Jack di tengah keramaian lebih cepat daripada mencari semua orang di kerumunan dan menghilangkan semua dari pertimbangan kecuali Jack.

S Renalds
sumber
Masalahnya adalah bahwa OP mengharapkan Jack menjadi satu-satunya orang di kerumunan. Jika ya, mereka beruntung. find jackakan daftar jackapakah itu file yang dipanggil jack, atau semua nama dalam direktori jika itu adalah direktori. Ini adalah kesalahpahaman tentang cara findkerjanya.
Kusalananda
1

Saya belum mengerti masalahnya tetapi bisa memberikan beberapa wawasan lagi.

Seperti untuk Kusalananda, find | greppanggilan itu jelas lebih cepat di sistem saya yang tidak masuk akal. Pada awalnya saya mengasumsikan semacam masalah buffering; bahwa menulis ke konsol memperlambat waktu ke syscall berikutnya untuk membaca nama file berikutnya. Menulis ke pipa sangat cepat: sekitar 40MiB / s bahkan untuk penulisan 32-byte (pada sistem saya yang agak lambat; 300 MiB / s untuk ukuran blok 1MiB). Jadi saya berasumsi bahwa finddapat membaca dari sistem file lebih cepat ketika menulis ke pipa (atau file) sehingga dua operasi membaca jalur file dan menulis ke konsol dapat berjalan secara paralel (yang findsebagai proses thread tunggal tidak dapat dilakukan sendiri.

Itu findsalah

Membandingkan dua panggilan

:> time find "$HOME"/ -name '*.txt' >/dev/null

real    0m0.965s
user    0m0.532s
sys     0m0.423s

dan

:> time find "$HOME"/ >/dev/null

real    0m0.653s
user    0m0.242s
sys     0m0.405s

menunjukkan bahwa findmelakukan sesuatu yang sangat bodoh (apa pun itu). Itu ternyata sangat tidak kompeten dalam mengeksekusi -name '*.txt'.

Mungkin tergantung pada rasio input / output

Anda mungkin berpikir itu find -namemenang jika sangat sedikit untuk ditulis. Tapi aku semakin memalukan find. Kehilangan bahkan jika tidak ada yang menulis sama sekali terhadap 200 ribu file (13 juta data pipa) untuk grep:

time find /usr -name lwevhewoivhol

findbisa secepat grep, meskipun

Ternyata findkebodohan dengan nametidak meluas ke tes lain. Gunakan regex sebagai gantinya dan masalahnya hilang:

:> time find "$HOME"/ -regex '\.txt$' >/dev/null     

real    0m0.679s
user    0m0.264s
sys     0m0.410s

Saya kira ini bisa dianggap bug. Adakah yang mau mengajukan laporan bug? Versi saya adalah find (GNU findutils) 4.6.0

Hauke ​​Laging
sumber
Seberapa berulang waktu Anda? Jika Anda melakukan -nametes pertama, maka mungkin lebih lambat karena isi direktori tidak di-cache. (Ketika pengujian -namedan -regexsaya menemukan mereka mengambil kira-kira waktu yang sama, setidaknya sekali efek cache telah dipertimbangkan. Tentu saja itu mungkin versi yang berbeda dari find...)
psmears
@psmears Tentu saja, saya telah melakukan tes ini beberapa kali. Masalah caching telah disebutkan bahkan dalam komentar atas pertanyaan sebelum jawaban pertama. findVersi saya adalah find (GNU findutils) 4.6.0
Hauke ​​Laging
Mengapa mengejutkan bahwa menambahkan -name '*.txt'memperlambat find? Itu harus melakukan pekerjaan ekstra, menguji setiap nama file.
Barmar
@Barmar Satu sisi, pekerjaan ekstra ini dapat dilakukan dengan sangat cepat. Di sisi lain pekerjaan ekstra ini menyimpan pekerjaan lain. findharus menulis lebih sedikit data. Dan menulis ke pipa adalah operasi yang jauh lebih lambat.
Hauke ​​Laging
Menulis ke disk sangat lambat, menulis ke pipa tidak terlalu buruk, hanya menyalin ke buffer kernel. Perhatikan bahwa dalam tes pertama Anda, menulis lebih banyak /dev/nullentah bagaimana menggunakan lebih sedikit waktu sistem.
Barmar
0

Perhatikan : Saya berasumsi maksud Anda find . -name filename(jika tidak, Anda mencari hal-hal yang berbeda; find filenamesebenarnya mencari ke jalur yang disebut nama file , yang mungkin hampir tidak berisi file, maka keluar dengan sangat cepat).


Misalkan Anda memiliki direktori yang menampung lima ribu file. Pada kebanyakan sistem file, file-file ini sebenarnya disimpan dalam struktur pohon , yang memungkinkan untuk dengan cepat menemukan satu file yang diberikan.

Jadi, ketika Anda meminta finduntuk mencari file yang namanya hanya membutuhkan memeriksa, findakan meminta untuk itu file, dan file itu saja, untuk filesystem yang mendasari, yang akan membaca sangat sedikit halaman dari mass storage. Jadi jika filesystem-nya bernilai garam, operasi ini akan berjalan jauh lebih cepat daripada melintasi seluruh pohon untuk mengambil semua entri.

Ketika Anda meminta dataran findnamun itu yang Anda lakukan, Anda melintasi seluruh pohon, membaca. Setiap. Tunggal. Masuk. Dengan direktori besar, ini mungkin menjadi masalah (itu persis alasan mengapa beberapa perangkat lunak, perlu menyimpan banyak file pada disk, akan membuat "pohon direktori" dalam dua atau tiga komponen: dengan cara ini, setiap daun hanya perlu menyimpan lebih sedikit file).

LSerni
sumber
-2

Mari kita asumsikan file / john / paul / george / ringo / beatles ada dan file yang Anda cari disebut 'batu'

find / stones

find akan membandingkan 'beatles' dengan 'stones' dan menjatuhkannya ketika 's' dan 'b' tidak cocok.

find / | grep stones

Dalam hal ini find akan melewati '/ john / paul / george / ringo / beatles' untuk grep dan grep harus bekerja jalan melalui seluruh jalan sebelum menentukan apakah itu cocok.

Oleh karena itu grep melakukan pekerjaan yang jauh lebih banyak dan karena itu dibutuhkan waktu lebih lama

Paranoid
sumber
1
Sudahkah Anda mencobanya?
Hauke ​​Laging
3
Biaya perbandingan string (sangat sederhana dan murah) sepenuhnya dikerdilkan oleh IO (atau hanya syscall jika di-cache) biaya pencarian direktori.
Mat
grep bukan perbandingan string, perbandingan ekspresi reguler yang artinya harus bekerja melalui seluruh string hingga menemukan kecocokan atau mencapai akhir. Pencarian direktori adalah sama apa pun yang terjadi.
Paranoid
@ Paranoid Hm, versi pencarian apa yang kamu bicarakan? Tampaknya bukan apa-apa seperti temuan yang saya gunakan di debian.
pipa