Uwe Ohse

Artikel

Schnellerer stat?


Diese Frage wurde gestellt:
>Ich muss grauenvoll viele Dateien in moeglichst kurzer Zeit stat()en. 
>Gibt es irgendeinen Trick (= irgendeine Funktion, die ich noch nicht 
>kenne), die Sache zu beschleunigen?
Viel kann man da nicht machen. stat() in grossen Verzeichnissen wird schon alleine durch die langsame sequentielle Suche des Betriebssystems durch das Verzeichnis genug gebremst. An diesem Umstand laesst sich ohne Weiteres nichts machen (es sei denn man verwendet ein Dateisystem mit intelligenteren Verzeichniszugriffsmethoden).
Man kann allerdings dem Kernel des Leben etwas einfacher machen. Zur Erklaerung ein Ausflug in die Mechanismen im Kernel:

Wenn der Kernel einen Dateinamen bekommt (um damit irgendetwas zu machen - Datei oeffnen, loeschen oder auch nur stat()ten) geht er etwa so vor:

0.
wenn Name mit / beginnt dann beginne Suche im Rootverzeichnis des Prozesses, sonst im aktuellen Verzeichnis.
1.
Nehme den Teil des Namens bis zum naechsten Slash (/), oder, wenn kein Slash mehr darin ist, den ganzen Namen. Suche danach im Suchverzeichnis. Nicht gefunden? -> Abbruch.
2.
Wenn ein Slash vorhanden war dann verlaengere den Suchverzeichnisnamen um den eben gefundenen Namensteil und mache mit dem Rest des Namens dann bei 1 weiter. (uebrigens werden ueberzaehlige Slashes einfach uebersprungen, //tmp/////abc ist also gleich /tmp/abc).
3.
Sonst hat man das gewuenschte Objekt gefunden.

Dazu kommen natuerlich noch Zugriffsrechtspruefungen. Was hier relativ einfach aussieht hat es in sich - der Teil Suche danach im Suchverzeichnis ist in jedem Fall nicht billig.

Hier kann man durch Wechseln in das Verzeichnis mit den zu suchenden Namen (chdir) etwas optimieren. Das bietet sich natuerlich besonders bei laengeren Pfaden oder grossen Verzeichnissen weiter oben an.
Aergerlicherweise gibt es hier noch eine weitere Falle - nachdem man das Verzeichnis gewechselt hat will man wahrscheinlich irgendwann wieder dahin zurueck wo man herkam. Dazu muss man natuerlich das alte Verzeichnis kennen, und wird dafuer getcwd() aufrufen. Diese Funktion ist aber extrem teuer, weil sie eine ganze Reihe teurer Funktionsaufrufe macht. Man sollte sie also sparsam einsetzen.

In haesslichem C ausgedrueckt:

	DIR *d;
	struct dirent *e;
	char ocwd[PATH_MAX];
	getcwd(ocwd,PATH_MAX) || exit(1);
	chdir("dir.mit.vielen.files") && exit(1);
	(d=opendir(".")) || exit(1);
	while ((e=readdir(d))!=NULL) {
		/* do something with e->d_name */
	}
	closedir(d) && exit(1);
	chdir(ocwd) && exit(1);

Wenn man kann sollte man das Problem auf andere Weise angehen, naemlich durch Verteilung der Daten auf nicht ein, sondern viele Verzeichnisse. Ideen kann man sich z.B. bei Netscape (dem Browser, .netscape/cache) oder squid holen. Andererseits sind solche Loesungen nur Workarounds um Dateisystemschwaechen, fuer die Allgemeinheit duerfte es besser sein das Problem an der Wurzel zu loesen. Leider ist die Implementierung besserer Mechanismen im Dateisystem nicht trivial.