Jonas Hauenstein: opendir und readdir Ressourcenfresser?

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

  1. 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

    1. 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

      1. 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

        1. 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

          1. 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

            1. 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

            2. 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

              1. 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