2009-04-09 6 views
3

Ich benutze Perl readdir, um Dateiauflistung zu erhalten, jedoch enthält das Verzeichnis mehr als 250.000 Dateien und dies führt zu langen Zeit (länger als 4 Minuten), um Readdir durchzuführen und verwendet über 80 MB RAM. Da dies ein wiederkehrender Job alle 5 Minuten sein sollte, wird diese Verzögerungszeit nicht akzeptabel sein.Wie kann ich Perls readdir für ein Verzeichnis mit 250.000 Dateien beschleunigen?

Weitere Informationen: Ein anderer Job füllt das Verzeichnis (einmal pro Tag), das gescannt wird. Dieses Perl-Skript ist verantwortlich für die Verarbeitung der Dateien. Für jede Skriptiteration wird eine Dateianzahl angegeben, derzeit 1000 pro Lauf. Das Perl-Skript wird alle 5 Minuten ausgeführt und verarbeitet (falls zutreffend) bis zu 1000 Dateien. Limit für die Anzahl der Dateien, damit die Downstream-Verarbeitung mithalten kann, wenn Perl Daten in die Datenbank verschiebt, was einen komplexen Workflow auslöst.

Gibt es eine andere Möglichkeit, Dateinamen aus dem Verzeichnis zu erhalten, im Idealfall auf 1000 begrenzt (durch Variable festgelegt), was die Geschwindigkeit dieses Skripts erheblich erhöhen würde?

Antwort

8

Was genau meinst du, wenn du sagst readdir dauert Minuten und 80 MB? Können Sie diese bestimmte Codezeile anzeigen? Verwenden Sie readdir im Skalar- oder Listenkontext?

Sind Sie so etwas wie dies zu tun:

foreach my $file (readdir($dir)) { 
    #do stuff here 
} 

Wenn das der Fall ist, lesen Sie das gesamte Verzeichnis in den Speicher auflistet. Kein Wunder, es braucht viel Zeit und viel Speicher.

Der Rest dieses Beitrags geht davon aus, dass dies das Problem ist, wenn Sie readdir im Listenkontext nicht verwenden, ignorieren Sie den Rest des Beitrags.

Das Problem ist, eine While-Schleife zu verwenden und readdir in einem skalaren Kontext zu verwenden.

while ( 
    defined(my $file = readdir $dir) 
) { 

    # do stuff. 

} 

Jetzt lesen Sie nur ein Element nach dem anderen. Sie können einen Zähler hinzufügen, um zu verfolgen, wie viele Dateien Sie auch verarbeiten.

+0

Brilliant. Ich muss vielleicht zurückgehen und einige Verzeichniszugriffe umgestalten! –

+1

das definierte Zeug ist implizit, während (meine $ file = readdir $ dir) {} ist OK –

+1

Das löste das Problem für mich. Erlaubt auch die genaue Kontrolle darüber, wie viele Dateinamen abgerufen wurden, um den Stopp bei der gewünschten Schwelle zu ermöglichen. Danke daotoad. – Walinmichi

0

Wahrscheinlich nicht. Ich denke, die meiste Zeit ist es, den Verzeichniseintrag zu lesen.

Sie könnten jedoch die gesamte Verzeichnisliste vorverarbeiten und eine Datei pro 1000 Einträge erstellen. Dann könnte Ihr Prozess jedes Mal eine dieser Auflistungsdateien ausführen und nicht die Kosten für das Lesen des gesamten Verzeichnisses aufwenden.

Haben Sie versucht nurreaddir() durch das Verzeichnis ohne irgendeine andere Verarbeitung überhaupt, um eine Grundlinie zu bekommen?

+0

Ja, die Daten, die ich zur Verfügung gestellt (> 4min) ist nur der readdir Betrieb. Ich habe die Prozessanzahl für den Test auf 1 festgelegt. – Walinmichi

7

Die Lösung vielleicht in anderen Ende liegen würde: im Skript, das das Verzeichnis füllt ...

Warum nicht eine arborescence erstellen alle diese Dateien zu speichern und auf diese Weise viele Verzeichnisse mit einer überschaubaren Anzahl jeder haben von Dateien?

Anstatt "mynicefile.txt" zu erstellen, warum nicht "m/my/mynicefile", oder etwas ähnliches?

Ihr Dateisystem würde Ihnen dafür danken (besonders wenn Sie die leeren Verzeichnisse entfernen, wenn Sie damit fertig sind). Diese

+1

+1, ich versuche in der Regel einen Ordner unter 1000 Dateien, mehr und Datei-System stat() Anrufe nur Stück selbst. –

+0

"Doktor, Doktor! Es tut weh, wenn ich mein Handgelenk berühre." "Nun, die Lösung ist einfach. Hör auf damit!" –

+0

Also, Herr Voodoo Arzt: Erzählen Sie uns von Ihrer magischen Lösung. Ich bin auch interessiert (aber will kein Tier in den Prozess opfern) – siukurnin

1

ist nicht gerade eine Antwort auf Ihre Frage, aber ich denke, die, dass viele Dateien im gleichen Verzeichnis ist nicht eine sehr gute Sache für die Gesamtgeschwindigkeit (einschließlich der Geschwindigkeit, mit der Ihr Dateisystem behandelt Operationen hinzufügen und löschen , nicht nur auflisten, wie Sie gesehen haben).

Eine Lösung für dieses Designproblem besteht darin, Unterverzeichnisse für jeden möglichen ersten Buchstaben der Dateinamen zu haben, und alle Dateien beginnen mit diesem Buchstaben innerhalb dieses Verzeichnisses. Berücksichtigen Sie bei Bedarf den zweiten, dritten usw. Brief.

Sie werden wahrscheinlich eine deutliche Geschwindigkeitsverbesserung bei den Mai-Operationen feststellen.

+0

Ich habe keine Kontrolle über die Datei Füllung, es ist nur ein FTP-Pull von Zip-Dateien, die dann unkomprimiert sind. Denken Sie darüber nach, ein weiteres Skript zu erstellen, das einmal pro Stunde ausgeführt wird, um eine einzelne Datei mit Dateinamen zu erstellen, die von dem häufigeren Veröffentlichungsskript verwendet werden. – Walinmichi

2

Sie sagen, dass der Inhalt durch das Entpacken der Zip-Datei (en) dorthin gelangt. Warum arbeiten Sie nicht einfach an den Zip-Dateien, anstatt 250k Dateien in einem Verzeichnis zu erstellen/zu verwenden?

Grundsätzlich - um es zu beschleunigen, brauchen Sie keine spezifische Sache in Perl, sondern auf Dateisystemebene. Wenn Sie 100% ig sicher sind, dass Sie mit 250k-Dateien im Verzeichnis arbeiten müssen (was ich mir nicht vorstellen kann, wenn so etwas nötig wäre), ist es besser, ein besseres Dateisystem zu finden, als es zu finden irgendein "magisches" Modul in Perl, das es schneller scannen würde.

+0

Ich verstehe nicht, wie man mit komprimierten ZIP-Dateien arbeitet. Übrigens gibt es viele Situationen, in denen ich sehr große Dateienmengen bearbeite ... normalerweise kein Problem, da ich das Dateihandle vielleicht kenne oder von einem anderen Prozess erhalte. In diesem Fall habe ich die Dateien aus einem anderen Prozess, der außerhalb meiner Kontrolle liegt, "abgelegt". – Walinmichi

+0

@unknown - Sie können Archive :: Zip verwenden, um mit ZIP-Dateien zu arbeiten. –

0

Sie werden readdir nicht beschleunigen können, aber Sie können die Überwachung eines Verzeichnisses beschleunigen. Sie können das Betriebssystem nach Updates fragen - Linux hat zum Beispiel inotify. Hier ist ein Artikel über die Verwendung:

http://www.ibm.com/developerworks/linux/library/l-ubuntu-inotify/index.html?ca=drs-

Sie Inotify von Perl verwenden können:

http://metacpan.org/pod/Linux::Inotify2

Der Unterschied ist, dass Sie eine lang laufende App anstelle eines Skripts haben, das ist angefangen von Cron. In der App halten Sie eine Warteschlange mit Dateien, die neu sind (wie von inotify bereitgestellt). Dann stellen Sie einen Timer ein, der alle 5 Minuten auslöst und 1000 Elemente verarbeitet. Danach kehrt die Steuerung zur Ereignisschleife zurück und Sie wachen entweder in 5 Minuten auf und verarbeiten 1000 weitere Elemente oder inotify sendet Ihnen weitere Dateien, die Sie der Warteschlange hinzufügen möchten.

(BTW, Sie erhalten eine Ereignisschleife müssen die Timer behandeln; ich EV empfehlen.)

Verwandte Themen