Delphi-PRAXiS
Seite 4 von 4   « Erste     234   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Viele Dateien performant einlesen (https://www.delphipraxis.net/180173-viele-dateien-performant-einlesen.html)

mensch72 29. Apr 2014 20:45

AW: Viele Dateien performant einlesen
 
Liste der Anhänge anzeigen (Anzahl: 1)
..."Ich habe eine Vielzahl (ein paar Tausend) CSV-Dateien. Die möchte ich allesamt auswerten."...

Nun ja, ich hätte da ein paar Millionen(~3Mio) Dateien(a ~8KB) in ein paar tausend Verzeichnissen(~180000) und renne da ständig durch...

wo ist das Problem:
- über alle Dateien
* FileStream öffnen und StreamSize ermitteln
* bei den "Spielzeuggrößen" mit einem Schlag vom FileStream in RAM Buffer einlesen
* FileStream freigeben(schließen)
* keine Verarbeitung oder sonst was!
* nächste Datei
- Zeit messen

Man könnte auch hier zwar per WinApi Funktionen und direktem RAM Puffer ein paar MicroSekunden sparen, aber FileStream reicht hier vollkommen aus.
(Nebenbei: Bei "großen Dateien" einfach mal fach TFastfileStream googeln... da findet sich dann was schönes mit MemoryMappedFiles:))

Wenn das OK, dann mit etwas Hirnschmalz ein eigenes "GetLine" geschrieben(einfach per BYTE Pointer durch den Rambuffer nach "CR" suchen und wenn CR gefunden es durch "NULL" ersetzen und die nun "NullTerminierte" Line ab vorherigem Pointer(bzw. anfangs "@0") weiter auswerten.

Gleiches Spiel nun innerhalb der Line. CSV-Trennzeichen fortlaufend suchen und gefundene Positionen+1 in BytePtrArray speichern und Inhalt des Pointers auf "0" setzen... Bingo: nun haben wir ohne jegliches Umkopieren irgendwelcher Speicher/Strings ein PtrArray mit lauter Pointern vom Typ PChar(bzw. PAnsiChar), welche wir problemlos verarbeiten, vergleichen, kopieren oder was auch immer können...


(Sorry für meine Vorliebe für NullTerminierte Strings auf Pointer-Basis, ich denke immer noch in "C" und nutze Delphi/Pascal nur weil es sein muss;))

TStringList ist zwar schön, aber im Masseneinsatz ist deren LoadFromXXX als "ReadLN" doof. Wenn es schnell gehen soll, dann 3 Threads... 2x FileStreamLoad, 1x "RAM-CSV-Explode"... Results dann in weiterem Thread speichern/verarbeiten... damit sollte QuadCore gut ausgelastet werden.

Blup 30. Apr 2014 12:12

AW: Viele Dateien performant einlesen
 
Zitat:

Zitat von Union (Beitrag 1257448)
Fragt sich was das für ein Cache ist? Wenn ich folgendes Fragment vorschalte, in dem ich die Daten komplett einlese, wird das Verarbeiten danach wesentlich schneller:
Delphi-Quellcode:
  for i := Low(AFiles) to High(AFiles) do
  begin
    Watch.Start;
    fs := TFileStream.Create(AFiles[i], fmExclusive);
    SetLength(Buffer, fs.Size);
    fs.ReadBuffer(Buffer[0], fs.Size);
    Watch.Stop;
    fs.Free;
  end;
Diese Funktion braucht für 5.000 frisch erzeugte Dateien zwischen 500-1500 ms.

Wenn ich dann die Daten nochmals in Stringlisten einlese, werden 605-615 ms verbraucht.

Starte ich das Einlesen in die Stringlisten ohne den Vorlauf, braucht das beim ersten Mal 12000-13000 ms.

Vorlauf1500
Verarbeitung615
Summe2115
Direkte Verarbeitung13000

D.h. dieses Verfahren ist 6x schneller!

Ich musste ein par Sachen anders lösen um das Programm mit Delphi 2007 zum Laufen zu bringen.
Die von dir gemessenen Ergebnisse kann ich aber überhaupt nicht nachvollziehen:
Code:
CreateDataClick
         CreateFiles 0:00:14.527
ReadClick
            GetFiles 0:00:00.007
           ReadFiles 0:00:01.692
               Split 0:00:03.112

CreateDataClick
         CreateFiles 0:00:09.681
OpenClick
            GetFiles 0:00:00.006
                Open 0:00:02.614
ReadClick
            GetFiles 0:00:00.006
           ReadFiles 0:00:01.649
               Split 0:00:03.336

p80286 30. Apr 2014 12:34

AW: Viele Dateien performant einlesen
 
Zitat:

Zitat von mensch72 (Beitrag 1257450)
Wenn das OK, dann mit etwas Hirnschmalz ein eigenes "GetLine" geschrieben(einfach per BYTE Pointer durch den Rambuffer nach "CR" suchen und wenn CR gefunden es durch "NULL" ersetzen und die nun "NullTerminierte" Line ab vorherigem Pointer(bzw. anfangs "@0") weiter auswerten.

Und was machst Du mit "dies sind[CRLF]zwei zeilen"?
Nach Deiner Methode mußt Du jede erstellte Zeile überprüfen ob sie vollständig ist, da doch lieber gleich richtig.

Gruß
K-H

Der schöne Günther 30. Apr 2014 13:08

AW: Viele Dateien performant einlesen
 
Doch, also bei mir ist es ähnlich:

Code:
openClick
GetFiles 50269 ticks, 16 ms
Open 745099 ticks, 238 ms


readClick
GetFiles 90555 ticks, 29 ms
ReadFiles 3411398 ticks, 1094 ms
Split 15388554 ticks, 4935 ms

stringListRead 3179206 ticks, 1019 ms
Delphi-Quellcode:
stringListRead
ist jetzt einfach nur ein TStringList-LoadFromFile, also im Endeffekt praktisch das gleiche wie ReadFiles nur mit einem leicht anderen
Delphi-Quellcode:
FileMode
zum Öffnen der Dateien.

Jetzt das Wichtige: Wir übersehen bei diesen Benchmarks hier alle den Windows-Cache hinter den Kulissen! Ich war grade auch verdutzt über meine Zeiten: Wie kam ich denn auf 45 Sekunden?

Dann habe ich mit RAMMap einmal den Windows-Datei-Cache geleert und statt ca. einer Sekunde zum Lesen bin ich nun wieder bei locker 30 Sekunden. Nur für das Lesen der Dateien.

Um aussagekräftige Messungen zu machen sollte man wohl vor jedem Durchgang einmal komplett den Windows File Cache leeren. Das werde ich im Verlauf des Tages auch nochmal machen...



Auf jeden Fall vielen Dank für die rege Teilnahme an alle! :thumb:

Dejan Vu 30. Apr 2014 13:57

AW: Viele Dateien performant einlesen
 
Zitat:

Zitat von p80286 (Beitrag 1257504)
Zitat:

Zitat von mensch72 (Beitrag 1257450)
...(einfach per BYTE Pointer durch den Rambuffer nach "CR" suchen und wenn CR gefunden es durch "NULL" ersetzen und die nun "NullTerminierte" ...

...da doch lieber gleich richtig....

Für das schnelle Lesen von CSV-Dateien gibt es hier doch eine schöne Lösung. Die kommt mit allen möglichen Sonderspezialfällen klar und ist zudem noch sehr schnell. Nur die Datei *einlesen* muss sie auch und da führt kein Weg dran vorbei.

Union 30. Apr 2014 15:48

AW: Viele Dateien performant einlesen
 
Die Antwort ist 134217728 ;)

Delphi-Quellcode:
procedure TForm1.btnOpenLowLevelClick(Sender: TObject);
var
  AFiles : TStringDynArray;
  fh : THandle;
  fl, fr : LongWord;
  i : integer;
  Buffer : Pointer;
begin
  Watch.Start;
  AFiles := TDirectory.GetFiles(FDir);
  Watch.Stop;
  Prot('GetFiles', Watch);
  Watch.Reset;
  for i := Low(AFiles) to High(AFiles) do
  begin
    Watch.Start;
    fh := CreateFile(PChar(AFiles[i]), GENERIC_READ,
      FILE_SHARE_READ, nil, OPEN_EXISTING,
      FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN, 0); // Optimiert für nur vorwärts lesen
    fl := GetFileSize(fh, nil);
    fr := 0;
    GetMem(Buffer, fl);
    ZeroMemory(Buffer, fl);
    ReadFile(fh, Buffer^, fl, fr, nil);
    CloseHandle(fh);
    FreeMem(Buffer);
    Watch.Stop;
  end;
  Prot('Lowlevel open', Watch);
  Watch.Reset;
end;
Damit liest er die 5000 Dateien in 160-400 ms komplett ein, auch wenn diese gerade frisch erzeugt wurden.

himitsu 30. Apr 2014 15:52

AW: Viele Dateien performant einlesen
 
Zitat:

auch wenn diese gerade frisch erzeugt wurden.
Gerade da liegen die ja vermutlich immernoch im Cache.

Zitat:

Optimiert für nur vorwärts lesen
Zum Glück hatte ich das nicht gleich am Anfang erwähnt. :angel:


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:22 Uhr.
Seite 4 von 4   « Erste     234   

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz