Mengapa "ls *" lebih lama dari "ls"?

28

Saya punya beberapa file dalam direktori:

$ ls | wc -l
9376

Adakah yang bisa menjelaskan mengapa ada perbedaan waktu yang sangat besar dalam menggunakan ls *dan ls?

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

dan

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

oke, ini adalah contoh drastis dan mungkin ditingkatkan karena direktori ada pada sistem file paralel umum (GPFS). Tapi saya juga bisa melihat penurunan yang signifikan pada sistem file lokal.

EDIT:

$ 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

dan saya harus menambahkan bahwa dalam contoh saya tidak ada sub direktori:

$ diff <(ls) <(ls *)
$
Sebastian
sumber

Jawaban:

47

Ketika Anda menjalankan lstanpa argumen, itu hanya akan membuka direktori, membaca semua konten, mengurutkannya dan mencetaknya.

Saat Anda menjalankan ls *, pertama shell mengembang *, yang secara efektif sama dengan apa yang sederhana lslakukan, membangun vektor argumen dengan semua file dalam direktori dan panggilan saat ini ls. lskemudian harus memproses vektor argumen itu dan untuk setiap argumen, dan memanggil access(2)¹ file untuk memeriksa keberadaannya. Maka itu akan mencetak output yang sama dengan yang pertama (sederhana) ls. Baik pemrosesan shell dari vektor argumen besar dan lskemungkinan akan melibatkan banyak alokasi memori blok kecil, yang dapat memakan waktu. Namun, karena ada sedikit sysdan userwaktu, tetapi banyak realwaktu, sebagian besar waktu akan dihabiskan menunggu disk, daripada menggunakan CPU melakukan alokasi memori.

Setiap panggilan ke access(2)perlu membaca inode file untuk mendapatkan informasi izin. Itu berarti lebih banyak disk membaca dan mencari daripada sekadar membaca direktori. Saya tidak tahu seberapa mahal operasi ini pada GPFS Anda, tetapi karena perbandingan yang telah Anda tunjukkan ls -lyang memiliki waktu berjalan yang sama dengan kasus wildcard, waktu yang diperlukan untuk mengambil informasi inode tampaknya mendominasi. Jika GPFS memiliki latensi yang sedikit lebih tinggi daripada sistem file lokal Anda pada setiap operasi baca, kami berharap itu akan lebih jelas dalam kasus ini.

Perbedaan antara kasus wildcard dan ls -l50% dapat dijelaskan oleh pemesanan inode pada disk. Jika inode diletakkan berturut-turut dalam urutan yang sama dengan nama file dalam direktori dan ls -lstat (2) ed file dalam urutan direktori sebelum menyortir, ls -lmungkin akan membaca sebagian besar inode dalam sapuan. Dengan wildcard, shell akan mengurutkan nama file sebelum meneruskannya ls, jadi lskemungkinan akan membaca inode dalam urutan yang berbeda, menambahkan lebih banyak gerakan kepala disk.

Perlu dicatat bahwa timeoutput Anda tidak akan mencakup waktu yang dibutuhkan oleh shell untuk memperluas wildcard.

Jika Anda benar-benar ingin melihat apa yang terjadi, gunakan strace(1):

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

dan lihat panggilan sistem yang dilakukan dalam setiap kasus.

¹ Saya tidak tahu apakah access(2)itu benar-benar digunakan, atau sesuatu yang lain seperti stat(2). Tetapi keduanya mungkin memerlukan pencarian inode (saya tidak yakin apakah access(file, 0)akan mem-bypass pencarian inode.)

camh
sumber
2
Jawaban yang bagus, saya baru saja akan memposting yang serupa :) Tapi ya, ini benar, ini semua tentang efisiensi dalam perulangan, dengan lsitu hanya dapat bertanya sistem file "apa yang anak-anak dari inode untuk pwd" di mana seperti dengan ls *itu harus bertanya "apa saja anak-anak (dan apa file) dari inode a" diikuti oleh b, c, d, dll. Satu pertanyaan vs banyak.
NJ
@NJ satu permintaan vs banyak adalah ringkasan yang baik sejauh ini. @camh: terima kasih atas jawaban terinci. Saya memposting output ls -ljuga (masih sekitar 30 detik kurang dari ls *)
Sebastian
@Sebastian Seperti yang dinyatakan camh, ls -lakan lebih lama dari yang lsdibutuhkan untuk stat(2)setiap file untuk mendapatkan informasi tentang stempel waktu / informasi pemilik / izin, dll.
NJ
6
Jangan lupa, *gumpal ke semua entri dalam direktori saat ini yang tidak dimulai dengan titik - termasuk nama subdirektori. Yang kemudian akan lsdiedit.
Shadur
@camh: Saya diuji sedikit lebih (lihat suntingan saya) dan menemukan bahwa: ls< ls -l< ls -l *< ls *(saya selalu berlari tiga kali). Dengan penjelasan Anda, saya tidak mengerti mengapa ls -l *lebih cepat darils *
Sebastian