opendir und readdir Ressourcenfresser?
Jonas Hauenstein
- perl
Hi
Die Frage stelle ich, da ich zur Anzeige von Bilderseiten bei jedem Aufruf einer entsprechenden Seite diese beiden Funktionen verwende. Nun hat mein Hoster sich beschwert, dieses Script würde zu viel Ressourcen fressen.
Wie sieht das aus? Ist es wirklich von der CPU Belastung her klüger, den Inhalt der Dirs zB in einer Datenbank zu speichern und dann die Daten von dort zu holen anstatt das Ganze direkt mittels readdir auszulesen?
Ich wäre um eine Antwort sehr froh. Denn bei mir lokal habe ich keine Probleme mit häufigen readdir Funktionen...
Gruss
Jonas
Halihallo Jonas
Wie sieht das aus? Ist es wirklich von der CPU Belastung her klüger, den Inhalt der Dirs zB in einer Datenbank zu speichern und dann die Daten von dort zu holen anstatt das Ganze direkt mittels readdir auszulesen?
Nein. readdir/opendir sind keine Ressourcenfresser. Eine Speicherung
in der Datenbank dürfte dir - bei exakt analoger Umsetzung - die
schlechteren Ergebnisse liefern.
Ich wäre um eine Antwort sehr froh. Denn bei mir lokal habe ich keine Probleme mit häufigen readdir Funktionen...
Das Script oder die Umgebung muss einen anderen Bug haben. Es kann an
deiner Programmierung (auch in Hinsicht auf readdir/opendir) liegen,
es kann an der Verzeichnisgrösse liegen, es kann noch mindestens an
1000 anderen Gründen liegen. Divide and conquer!
use Benchmark # to identify and isolate the problem
Ein kleiner Tipp zum Schluss: Mit Ressourcen muss nicht nur die
CPU-Belastung gemeint sein! - Hat dein Hoster sich nicht genauer
ausdrücken können? Welche Ressourcen sind denn gemeint?
Viele Grüsse
Philipp
Hi Philipp
Danke für die ausführlichen Infos.
Mein Hoster meinte explizit die CPU-Belastung, nicht den Speicher oder sonst was.
Nun, wenn das Problem nicht direkt bei opendir/readdir liegt, dann muss der Hacken entweder beim Hoster sein oder aber (wie du bereits sagtest) beim Script an anderer Stelle..
Im Grunde öffne ich das Dir, lese den Content in zwei arrays aus (einen array für die files, einen für die subdirs), danach lese ich die subdirs (meist zwei) auf files aus und füge diese einträge dem Array für die einzelnen Files von vorher hinzu. Zum schluss wird das array der files mit foreach abgearbeitet und alles in html ausgegeben.
Ich denke, es sollte also keine zu grossen Schleifen oder ähnliches im Script geben...
Gruss und Dank
jonas
Halihallo Jonas
Im Grunde öffne ich das Dir, lese den Content in zwei arrays aus (einen array für die files, einen für die subdirs), danach lese ich die subdirs (meist zwei) auf files aus und füge diese einträge dem Array für die einzelnen Files von vorher hinzu. Zum schluss wird das array der files mit foreach abgearbeitet und alles in html ausgegeben.
Naja, wenn du nicht tausende Unterverzeichnisse und Dateien hast,
geht das rucki zucki. Unglücklich finde ich den Ansatz alles in ein
oder zwei Arrays zu packen. Das kostet Unmengen RAM.
Ich denke, es sollte also keine zu grossen Schleifen oder ähnliches im Script geben...
Eine Endlosschleife kann auch sehr klein sein: while(1){}, je nach
System kann dies zu 100% CPU-Auslastung kommen. Ein while (1) {fork;}
dürfte das System noch wesentlich mehr in die Knie zwingen... Du siehst:
die Grösse der Schleife ist nicht relevant :-)
Nun gut, ich weiss was du meinst. Wenn es nicht viele
Unterverzeichnisse und Dateien sind, ist es kein Problem. Es sei denn
du verfolgst als "Unterverzeichnis" auch ".." oder "." (was bei
readdir ausgegeben wird!) dann, ja dann hast du eine Endlosschleife!
Aber dann dürfte es bei dir zu Hause auch nicht funktionieren...
Ich denke, dass die Seite einfach oft aufgerufen wird und der
Hoster dein Script als meist-ressourcen-verbrauchend (kummulativ)
eingestuft und entsprechend reagiert hat... Wenn die Abarbeitungsgeschwindigkeit des Scriptes (online und offline)
gut ist, sag deinem Hoster, dass das Script keine Fehler und
keine grosse Performance braucht und auf einigen getesteten Platformen
keine grosse CPU Auslastung nach sich ziehe. Mal sehen, was er dann
sagt... Der soll ruhig auch nochmal bei sich selber nachsehen, bevor
er Kunden (meiner Meinung nach) belästigt. Das gilt natürlich nur
dann, wenn es auch wirklich nicht dein Fehler ist...
Viele Grüsse
Philipp
Hi Phil
Danke für die erneut ausführliche Antwort.
Ich habe sicherlich keine Endlosschleifen drin. Auch die Unterverzeichnisse "." und ".." werden gar nicht erst in den Array aufgenommen. Und es werden maximal 3 Verzeichnisse ausgelesen. Also nehme ich mal als Schlussfolgerung an, dass es nicht an unserem Script bzw. an der opendir/readdir Funktion liegt!
Unglücklich finde ich den Ansatz alles in ein
oder zwei Arrays zu packen. Das kostet Unmengen RAM.
Was wäre da besser? Mehr Elemente in einem Array oder mehr Arrays für weniger Elemente?
Schöne Grüsse und Danke für die vielen Infos
Jonas
Halihallo Jonas
Unglücklich finde ich den Ansatz alles in ein
oder zwei Arrays zu packen. Das kostet Unmengen RAM.
Was wäre da besser? Mehr Elemente in einem Array oder mehr Arrays für weniger Elemente?
Nun, du sagtest, dass das Array mit den Dateien mit foreach
verarbeitet werden würde, es liegt also nahe komplett ohne Arrays
auszukommen: Stichwort Rekursion.
Die Rekursion[1] sähe schematisch wie folgt aus:
function doOutputFile(f) {
// das, was innerhalb der foreach-Schleife steht...
// Ausgabe der Datei
}
function doProcessDir(dir) {
// öffne 'dir'
while r:=record_in_directory
if (r eine Datei) {
doOutputFile(dir.'/'.r);
}
if (r ein Verzeichnis){
doProcessDir(dir.'/'.r);
}
end while
}
Arrays brauchen nunmal Speicher und wenn du tausende von
Unterverzeichnissen verarbeiten würdest, geht das auf den Speicher.
Deshalb sollte man, wenn möglich auf eine Array-basierte Verarbeitung
verzichten, falls es keine grossen Nachteile birgt. Aber bei drei
oder weniger Unterverzeichnissen mit jeweils weniger als ein kleines
X an Dateien, dürfte die Array-Variante nicht viel schlechter sein.
Bei sehr grossen Directory-Verschachtelungen (viele
Unterverzeichnisse) kommt man aber bei beiden Varianten an Grenzen
(bei der Rekursion aber erst sehr, sehr spät).
[1] leider keine Tail-Recursion, sonst wäre sogar eine gute iterative
Verarbeitung möglich.
Viele Grüsse
Philipp
Halihallo
[1] leider keine Tail-Recursion, sonst wäre sogar eine gute iterative
Verarbeitung möglich.
Nur dass ich nicht korrigiert werde:
Es gibt eine iterative Lösung, jedoch birgt diese andere Nachteile.
Zudem ist sie etwas umständlich und deshalb für normale Anwendungen
nicht anzuwenden. Es geht hier lediglich darum auch Worst-Case
Szenarien abdecken zu können, die jedoch in der Praxis (fast) nicht
vorkommen.
Viele Grüsse
Philipp
Moin!
Unglücklich finde ich den Ansatz alles in ein
oder zwei Arrays zu packen. Das kostet Unmengen RAM.
Was wäre da besser? Mehr Elemente in einem Array oder mehr Arrays für weniger Elemente?
Der Ansatz mit dem Array ist in meinen Augen sehr gut. Filesystemoperationen benötigen immer wesentlich mehr Zeit und wirken sich insbesondere auf einem Shared Server negativ auf alle anderen parallelel Prozesse aus, die ebenfalls Festplattenzugriffe ausführen wollen, so dass man Ergebnisse in jedem Fall zwischenspeichern sollte.
Und auch was die Datenmengen angeht, sind Arrays mit Sicherheit nicht die Übeltäter hier. Was wollen wir mal annehmen? 1.000 Dateien insgesamt? Mit jeweils 64 Zeichen langen Dateinamen. Macht an Datenmenge ungefähr 64 Kilobyte, vielleicht zusammen mit Verwaltungsinformationen für das Array 128 Kilobyte.
So ein Zwischenspeicher macht natürlich nur Sinn, wenn man diese Informationen danach noch mehrfach benötigt. Ein simples Directorylisting sollte sowas nicht erfordern.
Nun, du sagtest, dass das Array mit den Dateien mit foreach
verarbeitet werden würde, es liegt also nahe komplett ohne Arrays
auszukommen: Stichwort Rekursion.
Rekursionen sind zwar für rekursive Probleme gerne genommen, haben aber ebenfalls das Problem, je nach Aufgabe verhältnismäßig viel Speicher zu verbrauchen - in diesem Fall aber nicht als Array, sondern auf dem Stack.
Arrays brauchen nunmal Speicher und wenn du tausende von
Unterverzeichnissen verarbeiten würdest, geht das auf den Speicher.
Wenn du tausende von Unterverzeichnissen per Rekursion verarbeitest, geht das ganz genauso auf den Speicher. :)
- Sven Rautenberg
Halihallo Sven
Nun, du sagtest, dass das Array mit den Dateien mit foreach
verarbeitet werden würde, es liegt also nahe komplett ohne Arrays
auszukommen: Stichwort Rekursion.Rekursionen sind zwar für rekursive Probleme gerne genommen, haben aber ebenfalls das Problem, je nach Aufgabe verhältnismäßig viel Speicher zu verbrauchen - in diesem Fall aber nicht als Array, sondern auf dem Stack.
Das ist bedingt richtig. Bedingt deshalb, da bei einer rekursiven
Lösung des Problem die Verzeichnistiefe linear zum Stackverbrauch
steht, aber eben nicht die Verzeichnisbreite (Anzahl Dateien und
Unterverzeichnisse innerhalb des Verzeichnisses). Da in der Praxis
die Verzeichnisbreite grösser ist als die Tiefe ist eine rekursive
Lösung (falls der verbrauchte Speicher gleich gross ist) preferabel.
Arrays brauchen nunmal Speicher und wenn du tausende von
Unterverzeichnissen verarbeiten würdest, geht das auf den Speicher.Wenn du tausende von Unterverzeichnissen per Rekursion verarbeitest, geht das ganz genauso auf den Speicher. :)
Das hängt wesentlich von der Verzeichnisstruktur ab. Einmal 100
Unterverzeichnisse zu haben kann vorkommen. Eine Verzeichnistiefe
von 100 zu erreichen kommt in der Praxis eher selten vor. Deshalb
habe ich im vorangegangenen Absatz festgehalten, dass die rekursive
Lösung in der Praxis doch sinnvoller ist (mit ein paar "aber...").
An Euch beide: Natürlich habe ich nur prinzipiell etwas gegen diese
Arraylösung. Im Konkreten habe ich nichts dagegen einzuwenden, wie
Sven es richtig festhält: the bottleneck lies somewhere else
(zumindest eben im konkreten Beispiel)
Viele Grüsse
Philipp