Wie beeinflusst die Reihenfolge der Argumente die Suchgeschwindigkeit?

7

Wie wirkt sich die Reihenfolge der Argumente in einem findAufruf auf die Geschwindigkeit der Ergebnisse aus?

Vergleiche zum Beispiel (A)

find -name dir -type d

und B)

find -type d -name dir

Oder eine andere Kombination von Argumenten (z . B. mit -oroder -and). Ich würde erwarten find, irgendwie schlau zu sein.

Ich habe versucht, einige Statistiken zu sammeln, indem ich sowohl A als auch B mit timeund 5 Wiederholungen ausgeführt habe. Jedoch mit

 11.86, 7.23, 5.25, 5.87, 7.16

für A und für B:

9.73, 6.56, 8.69, 7.14, 6.35

Dies ist nicht wirklich schlüssig, mit Mittelwerten 7.5sfür beide und einer ziemlich hohen Varianz.

Um meine Frage zu wiederholen, spielt die Reihenfolge der Argumente eine Rolle find?

Bernhard
quelle
Es gibt definitiv Befehle, die den Satz von Dateien und Verzeichnissen reduzieren, wenn find Parsing-Befehle verwendet, aber ich bin mir nicht ganz sicher, ob die Reihenfolge von -name ..und von -type dBedeutung ist. Ihre Tests scheinen darauf hinzudeuten, dass dies nicht der Fall ist.
slm
@slm Ich interessiere mich für mehr als dieses spezielle Beispiel. Aber ich kann auch nicht die großen Unterschiede zwischen den Timings erklären ...
Bernhard
Ich würde eine Strace verwenden, um zu sehen, was der zugrunde liegende Fund vorhat, um weiter zu diagnostizieren.
slm
@slm siehe meine Antwort. Das scheint nicht zu helfen.
Terdon
Ich würde dann zu so etwas wie Fatrace, launchpad.net/fatrace, wechseln . Auf diese Weise können Sie die findauslösenden Dateiereignisse verfolgen, die Sie zu einem genaueren Bild davon führen sollten, welche Dateien während der findAusführung "berührt" werden .
slm

Antworten:

11

Was teuer ist, sind Systemaufrufe für die Dateien (für die Systemaufrufe selbst und für die E / A).

Solche Dinge -type, -mtimeerfordern einen lstat(2)auf dem Dateisystemaufruf. -name, -path, -regexNicht (obwohl natürlich wird es getan Systemaufrufe auf die Verzeichnisse haben sie enthalten , ihre Inhalte zu lesen).

In der findRegel wird eine lstat()ohnehin (weil es wissen muss, ob eine Datei ein Verzeichnis ist oder nicht, um in sie abzusteigen, es sei denn, diese Informationen sind im enthalten readdir()), aber es gibt Fälle, in denen es ohne sie auskommen kann. Wenn beispielsweise die Anzahl der Links eines Verzeichnisses weniger als 3 beträgt, findweiß es in einigen Dateisystemen, dass es keine Unterverzeichnisse hat, und einige findImplementierungen werden optimiert, indem dort kein lstats ausgeführt wird.

-xtypewird ein verursachen stat(2), -printf ..., -lsverursachen ein stat(), lstat(), readlink(), -lnamea lstat()und readlink().

Deshalb möchten Sie vielleicht das -name/ -path/ -regex... an die erste Stelle setzen. Wenn sie eine Datei ausschließen können, können sie einen oder mehrere Systemaufrufe vermeiden.

Nun, ein ist -regexvielleicht teurer als ein -name, aber ich bin mir nicht sicher, ob Sie durch den Austausch viel erreichen würden.

Beachten Sie auch, dass einige findImplementierungen wie GNU finddie Prüfungen nach Möglichkeit standardmäßig neu anordnen. Sehen:

info find 'Optimisation Options'

auf einem GNU-System ( dort auf gnu.org für die neueste Version von GNU findutils).

Wenn Sie Ihre Tests auf einem GNU-System durchgeführt hätten, würden beide Befehle normalerweise dasselbe tun, da findsie den -nameVorgang ohnehin vorwärts verschoben hätten .

Damit das -type d -name ...vs -name ... -type deinen Unterschied macht, benötigen Sie eine findImplementierung, die nicht durch Neuanordnen dieser Prädikate optimiert wird, und eine Implementierung, die einige Optimierungen vornimmt, indem nicht lstat()für jede Datei eine durchgeführt wird.

Wo es unabhängig von der Implementierung einen (großen) Unterschied geben wird, ist:

find . -name 'x*' -exec test -d {} \; -print

vs:

find . -exec test -d {} \; -name 'x*' -print

findIch kann das nicht neu anordnen, -execda dies zu Funktionsunterschieden führen kann ( findich kann nicht wissen, ob der ausgeführte Befehl nur zum Testen dient oder etwas anderes tut).

Und natürlich -exec ... {} \;ist es mehrere Größenordnungen teurer als jedes andere Prädikat, da es bedeutet, einen Prozess zu verzweigen und einen Befehl darin auszuführen (der selbst viele Systemaufrufe ausführt) und auf ihn und seinen Exit-Code zu warten.

$ time find /usr/lib -exec test -d {} \; -name z\* -print > /dev/null
1.03s user 12.52s system 21% cpu 1:03.43 total
$ time find /usr/lib -name z\* -exec test -d {} \;  -print > /dev/null
0.09s user 0.14s system 62% cpu 0.367 total

(Die erste ruft testjede Datei in /usr/lib(56685) auf, die zweite nur für die Dateien, deren Name mit z(147) beginnt .)

Beachten Sie, dass dies -exec test -d {} \;nicht dasselbe ist wie -type d. Es ist das tragbare Äquivalent der GNU-spezifischen -xtype d.

Stéphane Chazelas
quelle
Wenn ich Ihren ersten Satz verstehe, sagen Sie, dass jede Datei / jedes Verzeichnis, das Teil der "gefundenen Menge" ist, durch einen entsprechenden Systemaufruf behandelt werden muss? Das Tätigen dieses Systemaufrufs ist eine der kostspieligeren Aufgaben. Für bestimmte Anrufe sind beispielsweise lstat (2) -Informationen erforderlich, oder?
slm
@slm, ich sage, wenn entschieden werden findmuss, ob eine Datei ausgewählt ist oder nicht, ist es besser, die Überprüfungen, die keinen Systemaufruf beinhalten, zuerst zu setzen.
Stéphane Chazelas
1
Der -execBeweis ist ziemlich nett :)
Bernhard