![]() |
AW: Viele Dateien performant einlesen
Zitat:
Imho kann man höchstens die Tatsache ausnutzen, das es sich um eine SSD handelt und die Dateien wirklich über mehrere Threads einlesen. Die Directory ist ja schnell gelesen und so ein Thread zum einlesen ist auch Einsfixdrei gebaut. Dann kann man doch einfach prüfen, ob es was bringt... |
AW: Viele Dateien performant einlesen
Zitat:
Wir hatten den Fall, der Katalog eines Herstellers besteht aus mehr als 1000 kleinen Dateien, die die täglichen Änderungen der Stammdaten enthalten und ständig erweitert werden. Unser Testsystem importiert alle Änderungen und wir erstellen ca. einmal im Jahr oder bei Bedarf eine große Datei. Wenn unsere Kunden diesen Katalog benötigen, wird bei der Installation sehr viel Zeit gespart. Neben der großen Datei müssen nur die nachfolgenden Änderungen importiert werden. |
AW: Viele Dateien performant einlesen
Bei diesem Ansatz kann man durch einen Thread etwas mehr herausholen.
Einer Klasse wird der Dateiname übergeben. Diese Klasse hat eine Eigenschaft, die diese Datei als TStringList (oder TStrings) zur Verfügung stellt. Allerdings wird beim Zugriff auf diese Eigenschaft erst der Inhalt der Datei geladen (wenn dies in der Zwischenzeit noch nicht erfolgt ist). Wenn sich nun jede Instanz an einen Thread hängt, der das Laden der Dateien in den Instanzen bewirkt, dann kann die Verarbeitung und das Laden der Datei in unterschiedlichen Threads erfolgen und die Zeit reduziert sich im optimalen Fall auf die reine Ladezeit / Verarbeitungszeit der Dateien (je nachdem, was länger dauert). Wenn das reine Laden 30 Sekunden dauert und die Verarbeitung 15 Sekunden, dann kann das gesamte Laufzeitverhalten bis auf 30 Sekunden reduziert werden. Das geht natürlich nur, wenn die Verarbeitung keine Festplatten-Aktionen erfordert, denn dadurch wird ja die Ressource Festplatte wieder eingebunden. UPDATE Wenn die reine Verarbeitung nur einen Bruchteil der gesamten Abarbeitung beansprucht, wird dieses Konstrukt aber uninteressant und schafft nur eine weitere Komplikation :) |
AW: Viele Dateien performant einlesen
Zitat:
|
AW: Viele Dateien performant einlesen
Zitat:
Gruß K-H |
AW: Viele Dateien performant einlesen
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.
|
AW: Viele Dateien performant einlesen
Zitat:
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. |
AW: Viele Dateien performant einlesen
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.
|
AW: Viele Dateien performant einlesen
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. |
AW: Viele Dateien performant einlesen
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:
Diese Funktion braucht für 5.000 frisch erzeugte Dateien zwischen 500-1500 ms.
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; 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.
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; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:55 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-2025 by Thomas Breitkreuz