Dlaczego „ls *” trwa o wiele dłużej niż „ls”?

28

Mam kilka plików w katalogu:

$ ls | wc -l
9376

Czy ktoś może wyjaśnić, dlaczego istnieje tak ogromna różnica czasu w używaniu ls *i ls?

$ time ls > /dev/null
real    0m0.118s
user    0m0.106s
sys     0m0.011s

i

$ time ls * > /dev/null
real    1m32.602s
user    0m0.233s
sys     0m0.438s

okej, jest to drastyczny przykład i może zostać ulepszony, ponieważ katalog znajduje się w ogólnym równoległym systemie plików (GPFS). Ale widzę także znaczne spowolnienie w lokalnym systemie plików.

EDYTOWAĆ:

$ time ls -l > /dev/null
real    0m58.772s
user    0m0.113s
sys     0m0.452s
$ time ls -l * > /dev/null
real    1m19.538s
user    0m0.252s
sys     0m0.461s

i powinienem dodać, że w moim przykładzie nie ma podkatalogów:

$ diff <(ls) <(ls *)
$
Sebastian
źródło

Odpowiedzi:

47

Kiedy biegniesz ls bez argumentów, po prostu otworzy katalog, przeczyta całą zawartość, posortuje je i wydrukuje.

Po uruchomieniu ls *najpierw powłoka rozszerza się *, co w rzeczywistości jest tym samym, co proste ls, buduje wektor argumentów ze wszystkimi plikami w bieżącym katalogu i wywołaniami ls. lsnastępnie musi przetworzyć ten wektor argumentów i dla każdego argumentu i wywołuje access(2)¹ plik, aby sprawdzić jego istnienie. Następnie wydrukuje to samo wyjście, co pierwsze (proste) ls. Zarówno przetwarzanie przez duży wektor argumentów, jak i proces powłoki lsbędą prawdopodobnie wymagać dużej alokacji pamięci małych bloków, co może zająć trochę czasu. Jednak, ponieważ nie było mało sysi userczasu, ale dużo realczasu, większość czasu byłaby spędził czekając na dysku, zamiast CPU robi alokacji pamięci.

Każde wywołanie access(2)będzie musiało przeczytać i-węzeł pliku, aby uzyskać informacje o uprawnieniach. Oznacza to, że o wiele więcej dysków czyta i szuka niż tylko czytanie katalogu. Nie wiem, jak drogie są te operacje na twoim systemie GPFS, ale ponieważ pokazane porównanie, ls -lktóre ma podobny czas działania do przypadku wieloznacznego, wydaje się, że czas potrzebny na odzyskanie informacji i-węzłowych wydaje się dominować. Jeśli GPFS ma nieco większe opóźnienie niż lokalny system plików podczas każdej operacji odczytu, spodziewalibyśmy się, że w tych przypadkach będzie bardziej wyraźny.

Różnicę między wielkością wieloznaczną a ls -l50% można wyjaśnić uporządkowaniem i-węzłów na dysku. Gdyby i-węzły były ułożone kolejno w tej samej kolejności, co nazwy plików w katalogu, a ls -lstat (2) edował pliki w kolejności katalogów przed sortowaniem, ls -lprawdopodobnie odczytałby większość i-węzłów podczas przeciągnięcia. Za pomocą symbolu wieloznacznego powłoka posortuje nazwy plików przed przekazaniem ich ls, więc lsprawdopodobnie odczyta i-węzły w innej kolejności, zwiększając ruch głowicy dysku.

Należy zauważyć, że twój timewynik nie będzie zawierał czasu potrzebnego powłoce na rozwinięcie symbolu wieloznacznego.

Jeśli naprawdę chcesz zobaczyć, co się dzieje, użyj strace(1):

strace -o /tmp/ls-star.trace ls *
strace -o /tmp/ls-l-star.trace ls -l *

i zobacz, które wywołania systemowe są wykonywane w każdym przypadku.

¹ Nie wiem, czy access(2)jest rzeczywiście używany, czy coś takiego jak stat(2). Ale oba prawdopodobnie wymagają wyszukiwania i-węzłów (nie jestem pewien, czy access(file, 0)pominie to wyszukiwanie i-węzłów).

camh
źródło
2
Dobra odpowiedź, właśnie miałem zamieścić podobny :) Ale tak, to prawda, chodzi o wydajność w pętli, dzięki lsczemu może po prostu zapytać system plików „po co są dzieci i-węzła pwd” gdzie ls *musi zapytać „jakie są dzieci (i jaki jest plik) i-węzła a”, a następnie b, c, d itd. itd. Jedno zapytanie vs. wiele.
NJ
@NJ jedno zapytanie w porównaniu do wielu jest jak dotąd dobrym podsumowaniem. @camh: dzięki za szczegółową odpowiedź. Opublikowałem ls -lrównież wyniki (wciąż o około 30 sekund mniej niż ls *)
Sebastian
@Sebastian Jak powiedział camh, uzyskanie informacji o znacznikach czasu / informacjach o właścicielu / uprawnieniach ls -lzajmie więcej czasu niż lsma to miejsce w stat(2)każdym pliku
NJ
6
Nie zapomnij, że *globusy zawierają wszystkie wpisy w bieżącym katalogu, które nie zaczynają się od kropki - w tym nazwy podkatalogów. Który następnie zostanie ls„wydany”.
Shadur
@camh: Testowałem trochę więcej (patrz moje edycje) i stwierdził, że: ls< ls -l< ls -l *< ls *(zawsze prowadził ją trzy razy). Z twoim wyjaśnieniem nie rozumiem, dlaczego ls -l *jest szybszy niżls *
Sebastian