Perché "ls *" impiega molto più tempo di "ls"?


28

Ho un paio di file in una directory:

$ ls | wc -l
9376

Qualcuno può spiegare perché c'è un'enorme differenza di tempo nell'uso ls *e ls?

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

e

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

ok, questo è un esempio drastico e forse migliorato perché la directory è su un file system parallelo generale (GPFS). Ma posso anche vedere un rallentamento significativo su un file system locale.

MODIFICARE:

$ 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

e dovrei aggiungere che nel mio esempio non ci sono sottodirectory:

$ diff <(ls) <(ls *)
$

Risposte:


47

Quando si esegue lssenza argomenti, si aprirà semplicemente una directory, leggerà tutti i contenuti, li ordinerà e li stamperà.

Quando esegui ls *, per prima cosa la shell si espande *, il che è effettivamente lo stesso di quello che ha fatto il semplice ls, crea un vettore argomento con tutti i file nella directory corrente e chiama ls. lsdeve quindi elaborare quell'argomento vettore e per ogni argomento e chiama access(2)¹ il file per verificarne l'esistenza. Quindi stamperà lo stesso output del primo (semplice) ls. Sia l'elaborazione della shell del vettore di argomenti di grandi dimensioni lsche la causa implicheranno probabilmente molta allocazione di memoria di piccoli blocchi, che può richiedere del tempo. Tuttavia, poiché non vi era poco syse useril tempo, ma un sacco di realtempo, la maggior parte del tempo sarebbe stato speso in attesa per il disco, invece di usare CPU a fare l'allocazione della memoria.

Ogni chiamata a access(2)dovrà leggere l'inode del file per ottenere le informazioni sull'autorizzazione. Ciò significa che molte più letture e ricerche su disco rispetto alla semplice lettura di una directory. Non so quanto siano costose queste operazioni sul tuo GPFS, ma come il confronto a cui hai mostrato ls -lche ha un tempo di esecuzione simile al caso jolly, il tempo necessario per recuperare le informazioni sull'inode sembra dominare. Se GPFS ha una latenza leggermente superiore rispetto al filesystem locale su ogni operazione di lettura, ci aspetteremmo che sia più pronunciato in questi casi.

La differenza tra il caso jolly e ls -ldel 50% potrebbe essere spiegata dall'ordinamento degli inode sul disco. Se gli inode fossero disposti in successione nello stesso ordine dei nomi dei file nella directory e ls -lstat (2) ed i file in ordine di directory prima dell'ordinamento, ls -lprobabilmente leggerebbero la maggior parte degli inode in uno sweep. Con il carattere jolly, la shell ordinerà i nomi dei file prima di passarli a ls, quindi lsprobabilmente leggerà gli inode in un ordine diverso, aggiungendo più movimento alla testa del disco.

Va notato che l' timeoutput non includerà il tempo impiegato dalla shell per espandere il carattere jolly.

Se vuoi davvero vedere cosa sta succedendo, usa strace(1):

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

e controlla quali chiamate di sistema vengono eseguite in ciascun caso.

¹ Non so se access(2)viene effettivamente utilizzato o qualcos'altro come stat(2). Ma entrambi probabilmente richiedono una ricerca di inode (non sono sicuro access(file, 0)che ignorerebbe una ricerca di inode.)


2
Buona risposta, stavo per pubblicarne uno simile :) Ma sì, è corretto, si tratta di efficienze nel looping, con il lsquale può semplicemente chiedere al file system "quali sono i figli dell'inode pwd" dove ls *deve chiedere "quali sono i bambini (e qual è il file) dell'inode a" seguito da b, c, d, ecc. ecc. Una query contro molte.
NJ,

@NJ una query contro molte è finora un buon riassunto. @camh: grazie per la risposta dettagliata. Ho anche pubblicato l'output di ls -l(ancora circa 30 secondi in meno di ls *)
Sebastian

@Sebastian Come affermato da Camh, ls -lci vorrà più tempo di lsquanto non sia necessario per stat(2)ogni file per ottenere informazioni su timestamp / informazioni sul proprietario / autorizzazioni, ecc.
NJ

6
Non dimenticare, *rimanda a tutte le voci nella directory corrente che non iniziano con un punto, inclusi i nomi delle sottodirectory. Che sarà poi ls'ed.
Shadur,

@camh: Ho provato un po 'di più (vedi le mie modifiche) e ha scoperto che: ls< ls -l< ls -l *< ls *(ho sempre fatto funzionare tre volte). Con la tua spiegazione, non capisco perché ls -l *sia più veloce dils *
Sebastian
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.