AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi Viele Dateien performant einlesen
Thema durchsuchen
Ansicht
Themen-Optionen

Viele Dateien performant einlesen

Ein Thema von Der schöne Günther · begonnen am 28. Apr 2014 · letzter Beitrag vom 30. Apr 2014
Antwort Antwort
Dejan Vu
(Gast)

n/a Beiträge
 
#1

AW: Viele Dateien performant einlesen

  Alt 29. Apr 2014, 11:51
Wenn die reine Verarbeitung nur einen Bruchteil der gesamten Abarbeitung beansprucht, wird dieses Konstrukt aber uninteressant und schafft nur eine weitere Komplikation
Genau, außer vielleicht bei einer SSD oder einer Platte mit mehreren Leseköpfen. Da könnte es etwas bringen.
  Mit Zitat antworten Zitat
Benutzerbild von p80286
p80286

Registriert seit: 28. Apr 2008
Ort: Stolberg (Rhl)
6.659 Beiträge
 
FreePascal / Lazarus
 
#2

AW: Viele Dateien performant einlesen

  Alt 29. Apr 2014, 12:38
... oder einer Platte mit mehreren Leseköpfen. Da könnte es etwas bringen.
Möglich aber unwahrscheinlich, da dann auch die Verwaltungsinformation "in der Nähe" der Daten liegen müßte. (alle Daten auf einem Zylinder)

Gruß
K-H
Programme gehorchen nicht Deinen Absichten sondern Deinen Anweisungen
R.E.D retired error detector
  Mit Zitat antworten Zitat
Benutzerbild von Union
Union

Registriert seit: 18. Mär 2004
Ort: Luxembourg
3.492 Beiträge
 
Delphi 7 Enterprise
 
#3

AW: Viele Dateien performant einlesen

  Alt 29. Apr 2014, 12:53
Letztendlich sind das alles Ratespiele. Warum nicht zielgerichtet vorgehen und einen Profiler einsetzen. Dann die Teile mit dem größten Einfluss auf die Performance optimieren, indem man z.b. Das Encoding selber setzt anstatt das die Stringliste selber machen zu lassen usw.
Ibi fas ubi proxima merces
sudo /Developer/Library/uninstall-devtools --mode=all
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#4

AW: Viele Dateien performant einlesen

  Alt 29. Apr 2014, 16:09
Letztendlich sind das alles Ratespiele. Warum nicht zielgerichtet vorgehen und einen Profiler einsetzen. Dann die Teile mit dem größten Einfluss auf die Performance optimieren, indem man z.b. Das Encoding selber setzt anstatt das die Stringliste selber machen zu lassen usw.
Hat er doch schon gemacht (siehe ersten Beitrag) aber nicht explizit aufgeschlüsselt, wieviel Zeit jeder Schritt beansprucht.

Mit der OmniThreadLibraray könnte man sich auch eine Pipeline bauen, wo Einlesen und Verarbeiten separate Schritte sind. Dann kann man darauf weiter aufbauen und jeden Schritt nochmals in sich optimieren.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Benutzerbild von Union
Union

Registriert seit: 18. Mär 2004
Ort: Luxembourg
3.492 Beiträge
 
Delphi 7 Enterprise
 
#5

AW: Viele Dateien performant einlesen

  Alt 29. Apr 2014, 17:28
Ja, aber er hat LoadFromFile nicht profiled. Wenn man das echt instrumentieren will, muß man notfalls eine Kopie davon machen, an der man profiled und optimiert. Zum Schluß hat man dann automatisch eine "Ladeoptimierte" TStringList.
Ibi fas ubi proxima merces
sudo /Developer/Library/uninstall-devtools --mode=all
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.313 Beiträge
 
Delphi 12 Athens
 
#6

AW: Viele Dateien performant einlesen

  Alt 29. Apr 2014, 21:01
Da es aber schnell genug läuft, wenn die Dateien schon im Cache liegen, dann ist das Encoding der TStringList vernachlässigbar.

Ansonsten hatte ich ja eigentlich schon alles erklärt.
Ein Therapeut entspricht 1024 Gigapeut.
  Mit Zitat antworten Zitat
Benutzerbild von Union
Union

Registriert seit: 18. Mär 2004
Ort: Luxembourg
3.492 Beiträge
 
Delphi 7 Enterprise
 
#7

AW: Viele Dateien performant einlesen

  Alt 29. Apr 2014, 21:31
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!

Komplettes Testprogramm:
Delphi-Quellcode:
unit main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Diagnostics;

type
  TForm1 = class(TForm)
    btnCreateData: TButton;
    btnRead: TButton;
    MemoProt: TMemo;
    btnOpen: TButton;
    procedure btnCreateDataClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnReadClick(Sender: TObject);
    procedure btnOpenClick(Sender: TObject);
  private
    { Private-Deklarationen }
    FDir : string;
    FWatch : TStopWatch;
    procedure Prot(const AMessage : string; AWatch : TStopWatch);
  public
    { Public-Deklarationen }
    property Watch : TStopWatch read FWatch;
  end;

var
  Form1: TForm1;

implementation

uses
  System.Types,
  System.IOUtils,
  System.StrUtils;

{$R *.dfm}

procedure TForm1.btnCreateDataClick(Sender: TObject);
var
  i : integer;
  Line : string;
begin
  Watch.Start;
  Line := Dupestring('ABCDEFGHI;', 2400);
  for i := 1 to 5000 do
    TFile.WriteAllText(TPath.Combine(FDir, Format('%4.4d.txt', [i])), Line);
  Watch.Stop;
  Prot('CreateFiles', Watch);
  Watch.Reset;
end;

procedure TForm1.btnOpenClick(Sender: TObject);
var
  AFiles : TStringDynArray;
  fs : TFileStream;
  i : integer;
  Buffer : TByteDynArray;
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;
    fs := TFileStream.Create(AFiles[i], fmExclusive);
    SetLength(Buffer, fs.Size);
    fs.ReadBuffer(Buffer[0], fs.Size);
    Watch.Stop;
    fs.Free;
  end;
  Prot('Open', Watch);
  Watch.Reset;
end;

procedure TForm1.btnReadClick(Sender: TObject);
var
  AFiles : TStringDynArray;
  i : integer;
  Line : string;
  sl : TStringList;
  SplitWatch : TStopWatch;
begin
  Watch.Start;
  AFiles := TDirectory.GetFiles(FDir);
  Watch.Stop;
  Prot('GetFiles', Watch);
  Watch.Reset;

  sl := TStringList.Create;
  sl.Delimiter := ';';
  try
    SplitWatch := TStopWatch.Create;
    for i := Low(AFiles) to High(AFiles) do
    begin
      Watch.Start;
      Line := TFile.ReadAllText(AFiles[i]);
      Watch.Stop;
      SplitWatch.Start;
      sl.DelimitedText := Line;
      SplitWatch.Stop;
    end;
    Prot('ReadFiles', Watch);
    Prot('Split', SplitWatch);
  finally
    SplitWatch.Reset;
    Watch.Reset;
    sl.Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FDir := TPath.Combine(TPath.GetDirectoryName(Application.ExeName), 'Data');
  TDirectory.CreateDirectory(FDir);
  FWatch := TStopWatch.Create;
end;

procedure TForm1.Prot(const AMessage : string; AWatch : TStopWatch);
begin
  MemoProt.Lines.Add(AMessage + ' ' +AWatch.ElapsedTicks.ToString+' ticks, '+AWatch.ElapsedMilliseconds.ToString+' ms');
end;
Ibi fas ubi proxima merces
sudo /Developer/Library/uninstall-devtools --mode=all

Geändert von Union (29. Apr 2014 um 21:34 Uhr)
  Mit Zitat antworten Zitat
mensch72

Registriert seit: 6. Feb 2008
838 Beiträge
 
#8

AW: Viele Dateien performant einlesen

  Alt 29. Apr 2014, 21:45
..."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.
Angehängte Grafiken
Dateityp: png MioFiles.png (14,7 KB, 38x aufgerufen)
  Mit Zitat antworten Zitat
Antwort Antwort

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:15 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz