Announcement

Collapse
No announcement yet.

Verzeichnisse schneller lesen

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Verzeichnisse schneller lesen

    Hallo,

    Wie bei den 2 vorherigen Diskussionen handelt es sich um das gleiche Projekt.

    Ich lese komplette Verzeichnisstrukturen diverser Medien ein, diese werden dann weiter verarbeitet.

    Unter dem folgenden Link ist der relevante Teil funktionsfähig als EXE inklusive Quelltext zu erhalten:

    http://www.ephreno.de/files/file_search.zip

    Ihr werdet sehen, das das Programm die Verzeichnisstruktur komplett und sauber in 2 Listboxen darstellt.

    Mein Problem: Beim Erfassen von 20-40GB Wechselmedien mit bis zu 60000 Dateien in teilweise absurd vielen Verzeichnissen, benötigt auch ein schneller Rechner ca. 2,5 Minuten zum scannen des Mediums.

    Das dauert erheblich zu lange.

    Seht euch bitte im Quelltext die Procedure: FindFiles an.
    Sie stammt aus Delphi-source... oder so und funktioniert perfekt.

    Gibt es eine Möglichkeit die Einlesegeschwindigkeit noch zu steigern?
    Oder ist das bereits das Maximum des machbaren?

    Gruss Fred Ziebell

  • #2
    <pre>

    procedure Scan(const Path,FileMask: String; PathList,FileList: TStrings);
    var
    SR: TSearchRec;
    begin
    PathList.Add(Path);
    if FindFirst(Path + FileMask, faAnyFile and not (faDirectory or faVolumeID), SR) = 0 then
    try
    repeat
    FileList.Add(SR.Name);
    until FindNext(SR) <> 0;
    finally
    FindClose(SR);
    end;
    if FindFirst(Path + '*.*', faAnyFile, SR) = 0 then
    try
    repeat
    if (SR.Attr and faDirectory <> 0) and
    (SR.Name <> '.') and (SR.Name <> '..') then
    Scan(Path + SR.Name + '\', FileMask, PathList,FileList);
    until FindNext(SR) <> 0;
    finally
    FindClose(SR);
    end;
    end;<br>

    function ExpandSlash(const Value: String): String;
    begin
    Result := Value;
    if Result[Length(Result)] <> '\' then
    Result := Result + '\';
    end;<br>

    procedure FindFiles(const Path,FileMask: String; PathList,FileList: TStrings);
    begin
    if Assigned(PathList) and Assigned(FileList) and (Path <> '') then
    try
    PathList.BeginUpdate;
    FileList.BeginUpdate;
    Scan(ExpandSlash(Path), FileMask, PathList, FileList);
    finally
    PathList.EndUpdate;
    FileList.EndUpdate;
    end;
    end;<br>

    procedure TForm1.NewSpeedButton1Click(Sender: TObject);
    begin
    FindFiles('c:\', '*.*', ListBox1.Items, ListBox2.Items);
    end;<br>

    </pre>

    Obiger Code liest auf meinem rechner innerhalb von 7.5 Sekunden <b>alle</b> Informationen ein. Dein Code benötigt dafür 14 sekunden und findet bestimmte Dateien bzw. Ordner überhaupt nicht. Wichtig:
    <li>beim suchen nach Ordnern reicht NICHT faDirectory aus, da FindFirst() eben keine Bitweise Abfrage der Attribute vornimmt. Ordner die als Versteckt oder System gekennzeichnet wurden werden mit faDirectory alleine nicht gefunden. Es muß immer (faDirectory or faSystem or faHidden) als Minimum selektiert wwrden.<br>
    <li>der TypCast Boolean(Attr and faDirectory) ist schlichtweg falsch. Durch dummen Zufall ist faDirectory=1 und Byte(True) = 1, deshalb funktioniert deine Code. Bei Boolean(Attr and faHidden) wird er aber fehlschlagen.
    <li>Gibt FindFirst(.., SR) = 0 zurück, und ausschließlich dann, sind die Windows-Finddaten in SR initialisiert und MÜSSEN freigebenen werden. D.h. wir müssen try finally Blocks nutzen damit keine Memoryleaks verbleiben falls eine Exception auftritt (z.B. Listen sind voll usw.). Desweiteren MUSS FindClose() aufgerufen werden aber NUR wenn FindFirst() erfolgreich ist. Dies ist kein OS-Problem sondern ein Problem in den verschiedenen Delphi RTL Sourcen. In einigen Delphi versionen wird SR durch FindFirst() IMMER initialisiert, egal ob erfolgreich oder nicht. Somit kann FindClose() immer korrekt arbeiten. In anderen Delphi Versionen (glaube D6) wird aber SR eben NICHT initialisiert falls FindFirst() fehlschlägt. Ein anschließender proforma Aufruf von FindClose() wird eine AV in Kernel32.dll zur Folge haben.
    <li>Die TStrings sollten mit Begin/Endupdate die Anzeige Aktulisierung unterbinden. Angenommen TStrings ist ListBox.Items dann liegt dahinter eine extrem inperformante Stringverwaltung des Windows-OS dahinter. TStringList's sollten IMMER bevorzugt werden da sie in diesem Falle extrem schneller sind. Erst wenn die Liste eingelesen wurde sollte mit LiestBox1.Iems.Assign(StringList) angezeigt wwrden. Oder besser noch Memo.Text := StringList.Text was die schnellste Methode ist.<br>

    Gruß Hage

    Comment


    • #3
      Statt ListBox.Items habe ich mal eine TStringList benutzt. Sie benötigte 5 Sekunden. D.h. ein Speedup von 50 Prozent. Angenommen die nutzt keine TListBox'es sondern ein eigenes Control das auf VCL-TStringLists basiert. Mit obigen Code würdest Du deine 2,5 Minuten auf 50 Sekunden reduzieren können.<br>
      Geht man weiter und nutzt KEINE TStringLists, sondern große Speicherbereiche die die Pfade,Filenamen nacheinander speichern, dann dürfte das nochmal die zeit reduzieren. Mit TStringList's hat es 5.4 sekunden gedauert, ohne irgendwelche Listen dauerte es 4.5 Sekunden<br>

      Gruß Hage

      Comment


      • #4
        Eine interessante Frage waere es, ob Threads auf Teilbaeume der Directories beschleunigend wirken

        Comment


        • #5
          Nur falls man Mehrprozessoren Systeme hat die mit einem OS genutzt werden das die Job auch unshared auf diese CPU's verteilen kann. Dann müsste aber auch das Medium das gescannt wird in der Lage sein in parallel zwei Job zu bedienen. Aber das dürfte wohl unwahrscheinlich sein. Sollte das OS die komplette FAT des Mediums nicht im Speicher halten dann muß der Zugriff auf die FAT geblockt ablaufen. Da aber das Scannen permanent die FAT ließt würden sich sogar auf einem Mehrprozessorsystem die einzelenen Jobs blockieren.<br>
          Auf Single-CPU Systemen wird eine threaded System noch schlechter bedient. Es stehen pro Minute 100% Kapazität zur Verfügung. Nun würden sich 2 Threads diese 100% teilen, d.h. jeder Thread hat 30 -x Sekunden Zeit und kann nur das lesen was 1 Thread in 30-x Sekunden lesen kann. X Zeit wird vom OS zusätzlich benötigt um die beiden Threads zu verwalten. Somit würde zwei Thread 60-2x Sekunden benötigen was ein Thread in 60 Sekunden schafft. Sollte innerhalb von 60 Sekunden ca. 10ms per Thread zu Verfügung stehen -1ms fürs OS, dann ergeben sich 60Sec - 2 * 6Sec = 48 Sekunden. D.h. 2 Threads würden in 60 Sekunden das schaffen was ein Thread in 48 Sekunden schafft. Die 10 ms per Thread und 1ms für das OS zum Threadmanagement sind weit untertrieben, bzw übertrieben. Im Normallfalle bekommt dr Thread ca. >0.01 ms Zeit aber ein Threadswitch kostet mehr als 100.000 Taktcyclen. Somit sind Threads hier eine schlechte Lösung.<br>
          Es sei denn, das Medium produziert erheblich lange geblockte Wartezeiten. Dann wäre ein Thread sinnvoll da während dieser Wartezeit die anderen Threads weiterlaufen können. Dies ist der zB. Fall bei Kommunikationen (COM,TCP/IP) usw. aber garantiert nicht beim Zugriff auf Festplatten, CD oder Bandlaufwerken.<br>

          Gruß Hagen

          PS: Allerdings bei all dieser Theorie, kann man sich gewaltig irren. Z.b. hängt obige Annahme in unserem Falle stark davon ab WIE und WO das OS die FAT Infos verwaltet. Sollte es diese im Speicher halten und dieser Speicher kann merhfach in parallel gelesen werden, jo dann könnten mehrere Threads auch schneller arbeiten.<br>
          Probieren geht über studieren, ich würde darauf setzen das die Threads weniger Performance zeigen.

          Comment


          • #6
            Erstmal extremen Dank....ich vergelt´s im nächsten Leben
            @Hagen:
            Das nicht alle Files gefunden werden, war beabsichtigt.
            Hidden- und System- und div. andere Files waren für mich nicht interessant, da es ich im Prinzip um einen Diskkatalog mit einigen spezifischen Funktionen handelt.
            Das bei einlesen das ständige Aktualisieren der Listboxen Zeit kostet, war mit klar. Das es sich um SO VIEL Zeit handelt, war mir nicht klar. Im Hauptprogramm sollen tatsächlcch auch keine solchen erscheinen, ich habe diese nur zu Testzwecken benutzt.
            Ich werde jetzt deine Routine in mein Hauptprogramm einbinden und und hoffe auf einen "zeitlichen Augenorgasmus".

            Vielen Dank
            Fred Ziebel

            Comment


            • #7
              Hi,
              @Hagen:

              Hmmmm.....Habe deine Routine jetzt ausprobiert. Sie funktioniert tadellos. Aber....

              Der Speed-Gewinn ist nicht direkt und reproduzierbar ersichtlich.

              Mal abgesehen davon, das wir natürlich unterschiedliche MEdien mit unterschiedlichem Inhalt und unterschiedlichen GEschwindigkeiten benutzen, Scheint mir der Windows-Cache(Smartdrive) einen gewaltigen Einfluß auf meine Tests zu haben. Daher muß ich das ganze wohl eher prozentual bewerten.

              Die Prozedur, die zuletzt startet, ist nach einem Drivechange immer die schnellere.....logisch, da sich das zuletzt gelsene Verzeichnis ganz oder teilweise im Cache befinden. Daran ändert auch ein erzwungener Drivechange nichts.

              Allerdings habe ich bei zwei ähnlich großen Cd´s (Die Borland DELPHI 5 Cd´S Deutsch und Englisch) deine Routine im Vorteil gesehen( nicht 50% und nicht ständig reproduzierbar).

              Da deine Routine aber den wesentlich prof. Eindruck macht auch was die Systemsicherheit angeht, werde ich diese für meine Zwecke passend umbauen.

              Nochmals BEsten Dank.

              Gruss Fred Ziebel

              Comment


              • #8
                Dank

                Comment

                Working...
                X