Neden “find | grep 'dosyaadı' ”, 'dosyaadı' bulmaktan” çok daha yavaş mı?


10

Her iki komutu da denedim ve komut find | grep 'filename' basit find 'filename' komuttan çok daha yavaş .

Bu davranış için uygun bir açıklama ne olurdu?


2
Her dosyayı bul ile listeliyorsunuz ve ardından verileri işlemek için grep'e aktarıyorsunuz. Kendi başına kullanılan bulma ile, çıktıyı ayrıştırmak için grep'e listelenen her dosyayı geçirme adımını kaçırıyorsunuz. Bu nedenle bu daha hızlı olacaktır.
Raman Sailopal

Ne anlamda daha yavaş? Komutların tamamlanması farklı zaman alıyor mu?
Kusalananda

1
Bunu yerel olarak çoğaltamıyorum. Eğer bir şey varsa, time find "$HOME" -name '.profile'rapor daha uzun bir süre time find "$HOME" | grep -F '.profile'. (17'ler ve 12'ler).
Kusalananda

2
@ Her zaman art arda koştum. 17 ve 12 saniye ortalamalardır. Ve evet, grepvaryasyon sonuçtaki herhangi bir yerde eşleşir find, oysa eşleştirme find -namesadece tam olarak eşleşir (bu durumda).
Kusalananda

2
Evet, find filename hızlı olur . Bunun bir yazım hatası olduğunu ve OP'nin demek istediğini sanıyordum find -name filename. İle find filename, sadece filenameincelenecek (ve başka hiçbir şey).
Kusalananda

Yanıtlar:


11

( findBurada GNU olduğunu varsayıyorum )

Sadece

find filename

olur sadece dönecekti, çünkü hızlı olmak filename, veya içinde isimler filenamebu ismi geçerli dizinde olmasaydı bu bir dizin veya bir hata yoksa. Çok hızlı bir işlemdir ls filename(ancak bir dizinse özyinelemeli filename).

Tersine,

find | grep filename

geçerli dizinden ve altındaki tüm adların findbir listesini oluşturmaya izin verir ve bu da filtrelenir. Açıkçası bu çok daha yavaş bir işlem olacaktır.grep

Ben ne olduğunu tahmin ediyorum aslında amaçlanan oldu

find . -type f -name 'filename'

Bu filename, geçerli dizinde veya altında herhangi bir yerde normal bir dosyanın adı olarak görünür .

Bu, hızlı (veya nispeten hızlı) olacaktır find | grep filename, ancak grepçözüm filename, her bir adın tam yoluyla, ne -path '*filename*'yapacağına benzer şekilde eşleşecektir find.


Karışıklık, nasıl findçalıştığının yanlış anlaşılmasından kaynaklanıyor .

Yardımcı program birkaç yol alır ve bu yolların altındaki tüm adları döndürür.

Ardından , dosya adı, yol, zaman damgası, dosya boyutu, dosya türü vb. Üzerinde etkili olabilecek çeşitli sınamalar kullanarak döndürülen adları kısıtlayabilirsiniz .

Dediğinde

find a b c

Sormak findüç yolları altında kullanılabilir her ismi listelemek için a, bve c. Bunlar geçerli dizindeki normal dosyaların adlarıysa, bunlar döndürülür. Bunlardan herhangi biri bir dizinin adı olursa, o dizindeki diğer tüm adlarla birlikte döndürülür.

Ben yaparken

find . -type f -name 'filename'

Bu, geçerli dizindeki ( .) ve altındaki tüm adların bir listesini oluşturur . Daha sonra adları normal dosyalarla, yani dizinlerle vb. İle kısıtlar -type f. Sonra filenamekullanarak eşleşen isimleri için bir kısıtlama daha var -name 'filename'. Dize filenamebir dosya adı globbing deseni olabilir, *.txt(sadece alıntı yapmayı unutmayın!).

Misal:

Aşağıdakiler .profileana dizinimde çağrılan dosyayı "buluyor" gibi görünüyor :

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

Ama aslında, yoldaki tüm isimleri döndürür .profile(sadece bir isim vardır ve bu dosyadadır).

Sonra cdbir seviye yukarı ve tekrar deneyin:

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

findKomut şimdi denilen herhangi yolu bulamıyor .profile.

Ancak, geçerli dizine bakmak ve sonra döndürülen adları yalnızca kısıtlamak için.profile alırsanız , oradan da bulur:

$ pwd
/home
$ find . -name '.profile'
./kk/.profile

1
find filenameSadece dönecekti filenameeğer filenametipi değildi dizine (veya yazın dizinin, ama herhangi bir giriş kendisini yoktu)
Stéphane Chazelas

2

Teknik olmayan açıklama: Bir kalabalığın içinde Jack'i aramak, bir kalabalığın içindeki herkesi aramaktan ve Jack dışındaki her şeyi göz ardı etmekten daha hızlıdır.


Sorun şu ki OP, Jack'in kalabalığın içindeki tek kişi olmasını bekliyor. Eğer öyleyse, şanslılar. adlı bir dosya olup olmadığını veya bir dizinse dizindeki tüm adları find jacklisteler . Nasıl çalıştığının yanlış anlaşılması . jackjackfind
Kusalananda

1

Sorunu henüz anlamadım, ancak daha fazla fikir verebilirim.

Kusalananda için find | grepolduğu gibi, sistemimdeki çağrı çok daha hızlıdır, bu da çok mantıklı değildir. İlk başta bir çeşit tamponlama problemi olduğunu varsaydım; konsola yazmanın sonraki dosya adını okumak için sonraki sistem çağrısına geçme süresini yavaşlatır. Bir boruya yazmak çok hızlıdır: 32 baytlık yazmalar için bile yaklaşık 40MiB / s (oldukça yavaş sistemimde; 1MiB blok boyutu için 300 MiB / s). Bu nedenle find, bir boruya (veya dosyaya) yazarken dosya sisteminden daha hızlı okuyabildiğini, böylece dosya yollarını okuyan ve konsola yazılan iki işlemin paralel çalışabileceğini varsaydım ( findtek bir iş parçacığı işlemi olarak kendi başına yapamaz).

Bu bir findhata

İki çağrıyı karşılaştırma

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

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

ve

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

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

bunun findinanılmaz derecede aptalca bir şey yaptığını gösterir (ne olursa olsun). Sadece yürütmede oldukça beceriksiz olduğu ortaya çıktı -name '*.txt'.

Giriş / çıkış oranına bağlı olabilir

find -nameYazacak çok az şey varsa, bunun kazanacağını düşünebilirsiniz . Ama bu sadece daha utanç verici oluyor find. Aşağıdakiler için 200K dosyaya (13M boru verisi) karşı hiçbir şey yazılmasa bile kaybeder grep:

time find /usr -name lwevhewoivhol

findgrepyine de olabildiğince hızlı olabilir

Bu findaptallığın namediğer testlere uzanmadığı ortaya çıkıyor . Bunun yerine normal ifade kullanın ve sorun ortadan kalktı:

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

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

Bu bir hata olarak kabul edilebilir sanırım. Hata raporu vermek isteyen var mı? Benim sürümüm find (GNU findutils) 4.6.0


Zamanlamalarınız ne kadar tekrarlanabilir? -nameÖnce testi yaptıysanız, dizin içeriğinin önbelleğe alınmaması nedeniyle test daha yavaş olabilir. (Test ederken -nameve -regexen azından önbellek etkisi göz önüne alındığında, kabaca aynı zaman aldıklarını görüyorum. Tabii ki sadece farklı bir versiyonu olabilir find...)
psmears 16

@psmears Elbette, bu testleri birkaç kez yaptım. Önbellek sorunu, ilk cevaptan önce soruya yapılan yorumlarda bile belirtilmiştir. Benim findsürüm find (GNU findutils) 4.6.0
Hauke ​​Laging

-name '*.txt'Eklemenin yavaşlaması neden şaşırtıcı find? Her dosya adını test ederek fazladan iş yapmak zorundadır.
Barmar

@Barmar Bir yandan bu ekstra işler son derece hızlı bir şekilde yapılabilir. Öte yandan, bu ekstra iş diğer işleri kurtarır. finddaha az veri yazmak zorundadır. Ve bir boruya yazmak çok daha yavaş bir işlemdir.
Hauke ​​Laging

Bir diske yazmak çok yavaştır, bir boruya yazmak o kadar da kötü değildir, sadece çekirdek arabelleğine kopyalar. İlk testinizde bir /dev/nullşekilde daha fazla yazmanın daha az sistem zamanı kullandığına dikkat edin .
Barmar

0

Uyarı : Kastettiğinizi varsayacağım find . -name filename(aksi takdirde farklı şeyler arıyorsunuz; find filenameaslında dosya adı adı verilen , neredeyse hiç dosya içermeyen, dolayısıyla gerçekten hızlı bir şekilde çıkabilen bir yola bakar ).


Beş bin dosya içeren bir dizininiz olduğunu varsayalım. Çoğu dosya sisteminde, bu dosyalar aslında herhangi bir dosyayı hızlı bir şekilde bulabilmenizi sağlayan bir ağaç yapısında saklanır .

Eğer sorduğunuzda Yani find, ismi denetimi gerektiren bir dosyayı bulmak için, findolacak sormak için o yığın depolama gelen çok az sayıda sayfayla okuyacak yatan dosya sistemi için, dosyanın ve sadece bu dosya. Yani dosya sistemi tuz değerinde ise, bu işlem tüm girişleri almak için tüm ağacı çevirmekten çok daha hızlı çalışacaktır .

Ova findistediğinde, tam olarak bunu yaparsın, tüm ağacı okuyarak okursun. Her. Tek. Giriş. Büyük dizinlerle, bu bir sorun olabilir (tam olarak birçok dosyayı diskte depolaması gereken birkaç yazılımın iki veya üç bileşen derinliğinde "dizin ağaçları" yaratacağı nedeni budur: bu şekilde, her bir yaprağın yalnızca daha az tutması gerekir Dosyalar).


-2

/ John / paul / george / ringo / beatles dosyasının var olduğunu ve aradığınız dosyanın 'taş' olduğunu varsayalım

find / stones

find 'beatles'ı' stone 'ile karşılaştırır ve' s 've' b 'eşleşmediğinde düşürür.

find / | grep stones

Bu durumda bulmak grep '/ john / paul / george / ringo / beatles' geçecek ve grep onun bir maç olup olmadığını belirlemeden önce tüm yol boyunca çalışmak zorunda kalacak.

grep bu yüzden çok daha fazla iş yapıyor, bu yüzden daha uzun sürüyor


1
Bunu denedin mi?
Hauke ​​Laging

3
Dize karşılaştırmalarının maliyeti (son derece basit ve ucuz) tamamen dizin aramalarının IO (veya yalnızca önbelleğe alınmışsa syscall) maliyeti tarafından gölgelenir.
Mat

grep bir dize karşılaştırması değildir, normal ifade karşılaştırması, bir eşleşme bulana veya sonuna ulaşana kadar tüm dize boyunca yolunu bulması gerektiği anlamına gelir. Ne olursa olsun dizin aramaları aynıdır.
Paranoyak

@Paranoid Hımm, hangi sürümü bulmak bahsediyorsun? Görünüşe göre debian'da alışkın olduğum buluntu gibi bir şey değil .
boru
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.