![]() |
TBufferedFileStream
Hallo!
Ich muss manchmal ziemlich kleine daten von grossen dateien einlesen und der FileStream von Delphi ist manchmal etwas lahm deshalb habe ich versucht es ein wenig zu verbessern :shock: Ich habe es TBufferedFileStream getauft, weil es einfach ein bisschen mehr buffert ohne dass man etwas machen muss. Es ist eigentlich immer schneller, vorallem bei kleineren daten also unter 32 bytes oder so ist es 5.5 mal schneller beim lesen, beim schreiben bis zu 7 mal schneller. Es kann ganz einfach verwendet werden, einfach in deinem Code überall TBufferedFileStream anstatt nur TFileStream schreiben :roll:
Delphi-Quellcode:
ich empfehle das allen, die für schnelle Streams aus dateien nicht selber einen Buffer holen möchten :pale:
unit BufferedFileStream;
interface uses Classes; type TBufferedFileStream = class(TFileStream) const MaxBufSize = 4096; private FBuffer: array[0..Pred(MaxBufSize)] of Byte; FBufOffset: Int64; FBufSize: Integer; FDirty: Boolean; protected procedure MoveBuffer(const NewOffset: Int64); virtual; procedure FlushBuffer; virtual; public destructor Destroy; override; function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; end; implementation { TBufferedFileStream } destructor TBufferedFileStream.Destroy; begin FlushBuffer; inherited; end; procedure TBufferedFileStream.MoveBuffer(const NewOffset: Int64); begin FlushBuffer; FBufOffset:= NewOffset; Seek(FBufOffset, soBeginning); FBufSize:= inherited Read(FBuffer, SizeOf(FBuffer)); end; procedure TBufferedFileStream.FlushBuffer; begin if FDirty then begin Seek(FBufOffset, soBeginning); inherited Write(FBuffer, FBufSize); FDirty:= False; end; end; function TBufferedFileStream.Read(var Buffer; Count: Integer): Longint; var Offset: Int64; Delta: Integer; begin if Count < MaxBufSize then begin Offset:= Seek(0, soCurrent); Delta:= Offset - FBufOffset; if (Delta < 0) or (Delta + Count > FBufSize) then begin MoveBuffer(Offset); Delta:= Offset - FBufOffset; end; Result:= FBufSize - Delta; if Result > Count then Result:= Count; if Result > 0 then begin Move(FBuffer[Delta], Buffer, Result); end; Seek(Offset + Result, soBeginning); end else Result:= inherited Read(Buffer, Count); end; function TBufferedFileStream.Write(const Buffer; Count: Integer): Longint; var Offset: Int64; Delta: Integer; begin if Count < MaxBufSize then begin Offset:= Seek(0, soCurrent); Delta:= Offset - FBufOffset; if (Delta < 0) or (Delta + Count > MaxBufSize) then begin MoveBuffer(Offset); Delta:= Offset - FBufOffset;; end; Result:= Count; if FBufSize < Delta + Count then FBufSize:= Delta + Count; if Result > 0 then begin Move(Buffer, FBuffer[Offset - FBufOffset], Result); FDirty:= True; end; Seek(Offset + Result, soBeginning); end else Result:= inherited Write(Buffer, Count); end; end. Liebe Grüsse Laufi |
Re: TBufferedFileStream
Du weißt aber, daß du jetzt mindestens 3 Cache in deinem Stream drinnen hast?
- dein Puffer - die WindowsFileCache - die Cache z.B. der Festplatte - ......... es ginge also noch etwas Flotter - entweder man nutzt die WFC besser aus und optimiert deren Verwaltung - oder man umgeht die WFC und nutzt auf der tieferen Ebene nur noch die eigene Cache. für ersteren Weg (Optimieren der WFC ... siehe ![]() Zitat:
![]() nutzen einen Puffer, nur ist der leider standardmäßig sehr suboptimal eingestellt (128 Byte), so daß er in diesem Fall eher bremst ... es sei den man gibt da einen eigenen "passerenden" Puffer an PSS: wenn du den Stream entweder nur zum Lesen oder Schreiben öffnen läßt, dann ließe sich die Pufferverwaltung wesenlich vereinfachen und es wären auch keine/weniger SEEKs nötig |
Re: TBufferedFileStream
An dieser Stelle sollte man zur Freude des Autors vielleicht nochmal erwähnen, dass die Jedis auch so einen gepufferten Stream haben. :mrgreen:
|
Re: TBufferedFileStream
Hi,
Zitat:
Definitiv ist ein Cache innerhalb des eigenen Programmes schneller als der von Windows. Für kleinere Dateien wäre es auch denkbar, einen TMemoryStream zu verwenden und erst per LoadFromFile zu laden und später per SaveToFile zurück zu schreiben. Ansonsten auch MMF: ![]() Gruß FAlter |
Re: TBufferedFileStream
Hallo!
Zitat:
Zitat:
Liebe Grüsse Laufi |
Re: TBufferedFileStream
Der TXMLReadWriteBuffer (himXML.pas) und die zugehörigen Prozeduren werden jeweils nur zum Lesen ODER Schreiben genutzt, also nicht gleichzeitig und da muß man erstmal keine Umschaltung zwischen Beidem einbauen und kann die Funktion geziehlt auf jeweils eines von Beidem optimieren.
Aber ich lese dort entweder die Datei "komplett" ein oder speichere sie nur auf die Platte. Random- oder Sequential-Access merkt man eigentlich erst sehr stark, wenn wirklich viel gelesen/schrieben wird und dieses nicht alles in die WFC paßt. Aber es optimiert zumindesent die Speicherverwaltung etwas. PS: Ich hatte vor ein paar Tagen mal wieder ein Backup gemacht und wenn da das Programm "fehlerhaft" mit der Cache umgeht, dann legt man schonmal Windows minutenlang (und länger) lahm, wenn man ~800 GB mit teilweise über 100 MB/s durch diese Cache durchjagt und insgesammt nur 4 GB RAM zur Verfügung hat. OK, es hat ja auch Vorteile, wenn man gleichzeitig lesen und schreiben kann, auch wenn ich fast nie soetwas benöige. Aber egal ob nun gleichzeitig gelesen und geschrieben wird, wird es mit deiner Variante wesentlich langsamer, als nur mit der WFC, wenn man wirklich mal quer durch die Datei sappt (RandomAccess). |
Re: TBufferedFileStream
Hi,
Zitat:
Gruß Felix |
Re: TBufferedFileStream
Zitat:
- sein Cache ist es aber wirklich, da er nur je einen Bereich puffert und diesen "größeren" Bereich neu einlesen muß, wenn die neue Position nicht da drinnen liegt, also im Extremfalle ließt er z.B. bei 32 KB Cache und 16 Byte zum Auslesen, 2048 Mal soviele Daten ein, wie nötig. - die WindowsFileCache hat mehrere Cachebereiche und demnach kann/wird es vorkommen, daß zufällig schon passende Bereiche geladen sind und es somit nicht jedesmal neu aus der Datei geladen werden müßte. Drum meinte ich ja, daß es sich mit jeweils nur einer Art (Lesen oder Schreiben) und möglichst sequentiellem Zugriff die eigene Cache sich da besser/leichter optimieren läßt ... vorallem wenn man nur einen Block in dieser Cache hat. |
Re: TBufferedFileStream
Ich wollte mal einen Fehler melden.
Folgendes Beispiel, bei dem ich zu Testzwecken die Konstante MaxBufSize auf 4 gesetzt habe:
Delphi-Quellcode:
Was steht am Ende in der Datei? Richtig: ABClang!
procedure TForm1.Button1Click(Sender: TObject);
var S: AnsiString; begin With TBufferedFileStream.Create('C:\Test.txt', fmCreate) do try S := 'ABC'; Write(S[1], Length(S)); S := 'Zu lang!'; Position := 0; Write(S[1], Length(S)); finally Free; end; end; Denn erst wird ya das 'Zu lang!' in die Datei geschrieben und erst am Ende der Puffer geleert in dem 'ABC' steht. Klar, man könnte jetzt sagen, wer so'n Müll macht wie ich bei dem Test hat selbst schuld, aber so bin ich halt :-D |
Re: TBufferedFileStream
Man könnte auch gleich das hier verwenden:
![]() Besonders die Funktionen "QueryBufferedStream" und "FreeBufferedStream" sind sehr praktisch.
Delphi-Quellcode:
{Speeds up the given stream using the TAdBufferStreamAdapter class. Simply call
this method and use the stream you want to accelerate as parameter. Calls of QueryBufferedStream and FreeBufferedStream may be cascaded.} procedure QueryBufferedStream(var AStream: TStream); {Frees a buffered stream and returns the original stream. Calls of QueryBufferedStream and FreeBufferedStream may be cascaded.} procedure FreeBufferedStream(var AStream: TStream); |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:52 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