AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

TBufferedFileStream

Ein Thema von Laufi · begonnen am 14. Sep 2009 · letzter Beitrag vom 16. Sep 2009
Antwort Antwort
Laufi

Registriert seit: 21. Mär 2006
86 Beiträge
 
#1

TBufferedFileStream

  Alt 14. Sep 2009, 15:16
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

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

Delphi-Quellcode:
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.
ich empfehle das allen, die für schnelle Streams aus dateien nicht selber einen Buffer holen möchten

Liebe Grüsse
Laufi
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

Re: TBufferedFileStream

  Alt 14. Sep 2009, 16:13
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 MSDN-Library durchsuchenCreateFile) hab ich mich in himXML entschieden, da man die Lese-/Schreibzugriffe für den anderen Weg (umgehen der WFC) etwas mehr koordiniert werden muß (dieses findet man aber im FileSplitter wieder, da dort der Datentransfer sehr gut vorhersehbar ist)


Zitat von Laufi:
ich empfehle das allen, die für schnelle Streams aus dateien nicht selber einen Buffer holen möchten
PS: die alten Pascalfunktionen um Delphi-Referenz durchsuchenAssginFile
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
$2B or not $2B
  Mit Zitat antworten Zitat
Benutzerbild von Mithrandir
Mithrandir
(CodeLib-Manager)

Registriert seit: 27. Nov 2008
Ort: Delmenhorst
2.379 Beiträge
 
#3

Re: TBufferedFileStream

  Alt 14. Sep 2009, 16:24
An dieser Stelle sollte man zur Freude des Autors vielleicht nochmal erwähnen, dass die Jedis auch so einen gepufferten Stream haben.
米斯蘭迪爾
"In einer Zeit universellen Betruges wird das Aussprechen der Wahrheit zu einem revolutionären Akt." -- 1984, George Orwell
  Mit Zitat antworten Zitat
Benutzerbild von FAlter
FAlter

Registriert seit: 21. Jul 2004
Ort: Ostfildern
1.096 Beiträge
 
FreePascal / Lazarus
 
#4

Re: TBufferedFileStream

  Alt 14. Sep 2009, 17:00
Hi,

Zitat von himitsu:
- dein Puffer
- die WindowsFileCache
- die Cache z.B. der Festplatte
- .........
Durch Setzen der MaxBufSize auf 64KiB oder größer schaltet man afaik den Windows-Cache für diesen Lesevorgang aus. 64 KiB ($10000) sollte der optimale Wert sein.

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: http://www.delphipraxis.net/internal...t.php?t=134059

Gruß
FAlter
Felix Alter
  Mit Zitat antworten Zitat
Laufi

Registriert seit: 21. Mär 2006
86 Beiträge
 
#5

Re: TBufferedFileStream

  Alt 14. Sep 2009, 18:14
Hallo!

Zitat von himitsu:
Optimieren der WFC ... siehe MSDN-Library durchsuchenCreateFile)
Meinst du wenn man da sagt sequential scan oder random access? Ich habe das mal probiert, aber habe fast nichts gemerkt

Zitat von himitsu:
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
wie meinst du das? ich sehe nur dass man es noch ein bisschen besser machen kann wenn man nur schreibt aber das macht man doch fast nie

Liebe Grüsse
Laufi
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

Re: TBufferedFileStream

  Alt 14. Sep 2009, 18:35
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).
$2B or not $2B
  Mit Zitat antworten Zitat
Benutzerbild von FAlter
FAlter

Registriert seit: 21. Jul 2004
Ort: Ostfildern
1.096 Beiträge
 
FreePascal / Lazarus
 
#7

Re: TBufferedFileStream

  Alt 14. Sep 2009, 18:42
Hi,

Zitat von himitsu:
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).
Ich denke in diesem Fall ist ein Cache (fast) immer Kontraproduktiv.

Gruß
Felix
Felix Alter
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

Re: TBufferedFileStream

  Alt 14. Sep 2009, 18:55
Zitat von FAlter:
Ich denke in diesem Fall ist ein Cache (fast) immer Kontraproduktiv.
Das kommt auf den Cache drauf an.

- 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.
$2B or not $2B
  Mit Zitat antworten Zitat
Benutzerbild von Deep-Sea
Deep-Sea

Registriert seit: 17. Jan 2007
907 Beiträge
 
Delphi XE2 Professional
 
#9

Re: TBufferedFileStream

  Alt 16. Sep 2009, 15:49
Ich wollte mal einen Fehler melden.
Folgendes Beispiel, bei dem ich zu Testzwecken die Konstante MaxBufSize auf 4 gesetzt habe:
Delphi-Quellcode:
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;
Was steht am Ende in der Datei? Richtig: ABClang!
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
Chris
Die Erfahrung ist ein strenger Schulmeister: Sie prüft uns, bevor sie uns lehrt.
  Mit Zitat antworten Zitat
Benutzerbild von igel457
igel457

Registriert seit: 31. Aug 2005
1.622 Beiträge
 
FreePascal / Lazarus
 
#10

Re: TBufferedFileStream

  Alt 16. Sep 2009, 17:36
Man könnte auch gleich das hier verwenden: http://andorra.cvs.sourceforge.net/v....7&view=markup

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);
Andreas
"Sollen sich auch alle schämen, die gedankenlos sich der Wunder der Wissenschaft und Technik bedienen, und nicht mehr davon geistig erfasst haben als die Kuh von der Botanik der Pflanzen, die sie mit Wohlbehagen frisst." - Albert Einstein
  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 19:14 Uhr.
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