AGB  ·  Datenschutz  ·  Impressum  







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

File IO mit dem Windows-API?

Ein Thema von HHick123 · begonnen am 4. Jun 2008 · letzter Beitrag vom 30. Jun 2008
Antwort Antwort
Seite 2 von 3     12 3      
shmia

Registriert seit: 2. Mär 2004
5.508 Beiträge
 
Delphi 5 Professional
 
#11

Re: File IO mit dem Windows-API?

  Alt 9. Jun 2008, 14:55
Zitat von HHick123:
Was ich bei TFileStream etwas schade finde, ist dass es eine "Eigenintelligenz" bezüglich der Größe der tatsächlich gelesenen Häppchen zu haben scheint, ...
TFileStream reicht Read und Write Aufrufe ziemlich direkt an das Betriebssystem durch. (Also TFileStream puffert nicht )
TFileStream.ReadBuffer -> TFileStream.Read -> FileRead() -> ReadFile() Win-API

Wenn man Byteweise liest, wird man trotz des geringen Overheads einen Leistungsverlust merken.
So ungefähr ab 256 Bytes gibt es kaum noch Unterschiede zu grösseren Blockgrössen.
Hier eine Messung:
Code:
write 1048576 blocks à 1 bytes: 5750 ms
write 524288 blocks à 2 bytes: 2547 ms
write 262144 blocks à 4 bytes: 1266 ms
write 131072 blocks à 8 bytes: 640 ms
write 65536 blocks à 16 bytes: 313 ms
write 32768 blocks à 32 bytes: 172 ms
write 16384 blocks à 64 bytes: 78 ms
write 8192 blocks à 128 bytes: 31 ms
write 4096 blocks à 256 bytes: 31 ms
write 2048 blocks à 512 bytes: 16 ms
write 1024 blocks à 1024 bytes: 0 ms
write 512 blocks à 2048 bytes: 0 ms
write 256 blocks à 4096 bytes: 15 ms
write 128 blocks à 8192 bytes: 0 ms
write 64 blocks à 16384 bytes: 0 ms
write 32 blocks à 32768 bytes: 0 ms
read 1048576 blocks à 1 bytes: 2813 ms
read 524288 blocks à 2 bytes: 1437 ms
read 262144 blocks à 4 bytes: 704 ms
read 131072 blocks à 8 bytes: 343 ms
read 65536 blocks à 16 bytes: 188 ms
read 32768 blocks à 32 bytes: 78 ms
read 16384 blocks à 64 bytes: 47 ms
read 8192 blocks à 128 bytes: 31 ms
read 4096 blocks à 256 bytes: 16 ms
read 2048 blocks à 512 bytes: 0 ms
read 1024 blocks à 1024 bytes: 0 ms
read 512 blocks à 2048 bytes: 15 ms
read 256 blocks à 4096 bytes: 0 ms
read 128 blocks à 8192 bytes: 0 ms
read 64 blocks à 16384 bytes: 0 ms
read 32 blocks à 32768 bytes: 0 ms
Andreas
  Mit Zitat antworten Zitat
HHick123

Registriert seit: 23. Jul 2007
73 Beiträge
 
#12

Re: File IO mit dem Windows-API?

  Alt 10. Jun 2008, 14:24
Hallo Leute,
Also ich bin mittlerweile mit TFileStream auf einen grünen Zweig gekommen.

Mit dem oben angesprochenen Tool "File Monitor" hab' ich die Plattenzugriffe von TFilestream (die tatsächlich zugegriffene "Häppchengroesse") beobachtet und in etwa folgendes gesehen:

- Im großen und ganzen scheint die TFileStream-Buffergrößen als Häppchengröße auf,
dies ist natürlich ein Problem beim sequenziellen lesen/schreiben von Datentypen, die nur wenige byte groß sind.

- Wenn man im Tool "File Monitor" auf "Advanced Output" umschaltet, sieht man ausserdem einen interessanten Effekt: Anscheinend wird zunächst ein direkterer Hardwarezugriff mittels "FASTIO_WRITE" bzw. "FASTIO_READ" versucht, wobei als "Häppchengröße" die TFileStream-Buffergröße versucht wird. Schlägt dieser fehl, so wird ein Zugriff über "IRP_MJ_WRITE" bzw. "IRP_MJ_READ" nachgeschoben, wobei andere (i.a kleinere) "Häppchengrößen" verwendet werden.

Der Punkt ist nun der: Auf meinem Notebook z.B., sehe ich, dass "FASTIO_READ" auch mit großen "Häppchengrößen" funktioniert. Ab einer Buffergröße von 512kB merke ich aber kaum mehr einen Performance-Gewinn mit TFileStream. Bei noch wesentlich größeren Buffergrößen (die anscheinend dann wirklich zu groß sind) fällt der Mechanismus aber, glaub' ich, wieder auf 65536 byte "Häppchengröße" zurück.

"FASTIO_WRITE" dürfte allerdings - zumindest auf meinem Notebook - nur mit einer Häppchengröße von bis zu 65536 bytes funktionieren. Darüber schlägt es fehl, und es wird "IRP_MJ_WRITE" mit 65536 byte "Häppchengröße" verwendet. Allerdings versucht er beim nächsten Buffer wieder zunächst ein "FASTIO_WRITE", dass wieder fehlschlägt, usw... Das könnte die Sache natürlich ein bischen ausbremsen, obwohl ich nicht weiss, in welcher Größenordnung die Zeit liegt, bis der Fehlschlag von "FASTIO_WRITE" feststeht.

Zusammengefasst hab ich den Eindruck, dass bei meinem Notebook die Performance beim Lesen ab einer TFileStream-Buffergröße von 512kB nicht mehr wesentlich zunimmt und die Performance beim Schreiben ab einer TFileStream-Buffergröße von ca. 65536 nicht mehr wesentlich zunimmt, ev. sogar minimal abnimmt. Anbei mein momentaner Code, wie ich singles, smallints und bytes sequenziell von/auf Platte lese/schreibe:

Für Verbesserungen, Bug-Reports, Ideen, wie man den Code schneller machen könnte wäre ich natürlich sehr dankbar!!

Viele Grüße,
Helmut
Angehängte Dateien
Dateityp: dpr test_191.dpr (398 Bytes, 11x aufgerufen)
Dateityp: pas myfastfile_320.pas (4,3 KB, 14x aufgerufen)
  Mit Zitat antworten Zitat
Apollonius

Registriert seit: 16. Apr 2007
2.325 Beiträge
 
Turbo Delphi für Win32
 
#13

Re: File IO mit dem Windows-API?

  Alt 10. Jun 2008, 14:29
Das FASTIO_WRITE, IRP_MJ_WRITE etc. ist meiner Meinung nach uninteressant, da das arg nach Aktionen auf Treiberebene aussieht. Alles in allem denke ich, dass es Windows bei Standard-IO so gut macht, wie es geht - also keine zusätzliche Pufferung einführen.
Die einzige neue Möglichkeit, die mir noch einfällt, sind Memory Mapped Files - Neutral General hat hier mal eine Unit gepostet, die entsprechende TStream-Nachfahren einführt.
Wer erweist der Welt einen Dienst und findet ein gutes Synonym für "Pointer"?
"An interface pointer is a pointer to a pointer. This pointer points to an array of pointers, each of which points to an interface function."
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

Re: File IO mit dem Windows-API?

  Alt 10. Jun 2008, 14:38
MMFs würde ich in diesem Zusammenhang nicht unbedingt als Optimierung ansehn ... bei denen sind die Zugriffe noch unberechenbarer, als bei "Standard"-FileIOs.




MSDN-Library durchsuchenCreateFile mit ReadFile/WriteFile und nahezu alles, was davon abgeleitet ist ... z.B. FileStreams, wo man auch die nötigen Parameter angeben kann, wäre wohl optimal genug (vom Aufwand her).

Mit den Flags MSDN-Library durchsuchenFILE_FLAG_SEQUENTIAL_SCAN und MSDN-Library durchsuchenFILE_FLAG_RANDOM_ACCESS (siehe MSDN-Library durchsuchenCreateFile) kann man Windows auch noch mitteilen, in welcher Art man auf die Datei zugreifen will, damit Windows sein Lese-/Speicher-/Cacheverhalten entsprechend optimaler einstellen kann.


PS:
Zitat:
dass ich die Files in möglichst großen Portionen auf die Platte schreibe
in welcher Größenordnung liegt denn bei dir der Puffer (die Portionen)?


PSS:
Zitat:
... und intensiv auf der Platte herumfuhrwerkt - einerseits soll's schneller gehen, andererseits tut mir die Platte leid)
muß unbedingt die ganze Zeit auf der Platte rumgekramt werden,
oder könnte man Teile komplett im RAM abarbeiten lassen?
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.
  Mit Zitat antworten Zitat
HHick123

Registriert seit: 23. Jul 2007
73 Beiträge
 
#15

Re: File IO mit dem Windows-API?

  Alt 10. Jun 2008, 16:52
Zitat von Apollonius:
Alles in allem denke ich, dass es Windows bei Standard-IO so gut macht, wie es geht - also keine zusätzliche Pufferung einführen.
Das wäre natürlich ein Argument, von TFileStream abzuweichen und direkt auf das Windows-API aufzusetzen. Ehrlich gesagt, war ich bisher einfach zu faul, mich diesbezüglich in die msdn einzulesen.

Zitat von himitsu:
Mit den Flags FILE_FLAG_SEQUENTIAL_SCAN und FILE_FLAG_RANDOM_ACCESS (siehe CreateFile ) kann man Windows auch noch mitteilen, in welcher Art man auf die Datei zugreifen will, damit Windows sein Lese-/Speicher-/Cacheverhalten entsprechend optimaler einstellen kann.
Das klingt natürlich sehr interessant!

Zitat von himitsu:
in welcher Größenordnung liegt denn bei dir der Puffer (die Portionen)?
Von den Algorithmen her möchte ich einfach sequenziell "file of single, file of byte, file of smallint" lesen beziehungsweise schreiben.

Zum Filezugriff verwende ich momentan TFileStream auf einen Lesebuffer von 512kB und
einen Schreibbuffer von 64kB, aus denen ich die singles/bytes/smallints
sequenziell heraushole/hineinschreibe. Zu Beginn dieses Threads hab' ich noch kleinere Buffer (32kB) benutzt, hatte anstatt TFileStream: BlockWrite AssignFile und Co. in Verwendung, und hatte ausserdem noch laufend unnötige Aufrufe von SetFileAttributes und ForceDirectories. Es wird' schon besser

Zitat von himitsu:
muß unbedingt die ganze Zeit auf der Platte rumgekramt werden,
oder könnte man Teile komplett im RAM abarbeiten lassen?
Die Daten haben z.B. eine insgesamte Größe von etwa 50GB, momentan seh' ich da nicht viel Chancen, denn sie werden in mehreren "passes" analysiert, d.h. die 50GB werden nach einem Berechnungsschritt wieder komplett für den nächsten Schritt benötigt.
Teilweise sind es sehr lange File (stundenlange Audioaufnahmen, die abgespeichert oder aber analysiert werden wollen), teilweise auch viele kleine Files (etwa 40kB)...

Viele Grüße,
Helmut
  Mit Zitat antworten Zitat
shmia

Registriert seit: 2. Mär 2004
5.508 Beiträge
 
Delphi 5 Professional
 
#16

Re: File IO mit dem Windows-API?

  Alt 10. Jun 2008, 17:24
Zitat von HHick123:
Das wäre natürlich ein Argument, von TFileStream abzuweichen und direkt auf das Windows-API aufzusetzen.
TFileStream puffert nicht, die Daten werden direkt an die Windows-API durchgereicht!
Hier die Messungen:
Zitat:
Windows API: write 1048576 blocks à 1 bytes: 5015 ms
TFileStream: write 1048576 blocks à 1 bytes: 5047 ms
Der Unterschied beträgt ganze 32ms, wenn man eine Datei mit einem MB byteweise schreibt.
Das waren über eine Million Schreibvorgänge.
Wenn man den optimalen Durchsatz erreichen möchte, dann muss man selbst (*) puffern und eine Puffergrösse von ~ 2 KB bereithalten.
Den Rest erledigt der Cache von Windows.

*) Es gibt schon gepufferte Streamklassen im Internet. Bei Google suchenTBufferedstream AND delphi
Andreas
  Mit Zitat antworten Zitat
HHick123

Registriert seit: 23. Jul 2007
73 Beiträge
 
#17

Re: File IO mit dem Windows-API?

  Alt 10. Jun 2008, 17:53
Zitat:
TFileStream puffert nicht, die Daten werden direkt an die Windows-API durchgereicht! Hier die Messungen:

Zitat:
Windows API: write 1048576 blocks à 1 bytes: 5015 ms
TFileStream: write 1048576 blocks à 1 bytes: 5047 ms
Also dann sehe ich für den Moment mal eine "selbstgepufferte-TFileStream-Variante" als vorläufige Lösung an (ungefähr so, wie ich sie oben als Datei angehängt habe)...

Meinst Du, ob man bei einer der "Windows API-Variante" mit diesem FLAG_FILE_SEQUENTIAL_SCAN von CreateProcess, das himitsu erwähnt hat, oder viellicht anderen Flags noch wesentliche Optimierungen zustandebringen könnte, die TFileStream nicht ermöglicht und so eine "selbstgepufferte-WindowsAPI-Variante" vielleicht Vorteile gegenüber der "selbstgepufferten-TFilestream-Variante" hätte?

Viel Grüße und vielen Dank für die bisherigen Analysen!
Helmut
  Mit Zitat antworten Zitat
shmia

Registriert seit: 2. Mär 2004
5.508 Beiträge
 
Delphi 5 Professional
 
#18

Re: File IO mit dem Windows-API?

  Alt 10. Jun 2008, 18:57
Zitat von HHick123:
Meinst Du, ob man bei einer der "Windows API-Variante" mit diesem FLAG_FILE_SEQUENTIAL_SCAN von CreateProcess, das himitsu erwähnt hat, oder viellicht anderen Flags noch wesentliche Optimierungen ...
Das könnte durchaus etwas bringen.
Dazu würde ich von der Klasse THandleStream ableiten:
Delphi-Quellcode:
TSeqFileStream = class(THandleStream)
public
  constructor Create(const FileName: string; Mode: Word);
  destructor Destroy; override;
end;

constructor TSeqFileStream.Create(const FileName: string; Mode: Word);
begin
  if Mode = fmCreate then
  begin
    FHandle := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, .....); // CreateFile aus der Windows-API ***
    if FHandle < 0 then
      raise EFCreateError.CreateResFmt(@SFCreateError, [FileName]);
  end else
  begin
    FHandle := CreateFile(PChar(FileName), .....); // CreateFile aus der Windows-API ***
    if FHandle < 0 then
      raise EFOpenError.CreateResFmt(@SFOpenError, [FileName]);
  end;
end;

destructor TFileStream.Destroy;
begin
  if FHandle >= 0 then FileClose(FHandle);
end;
Die beiden Aufrufe für CreateFile() wären noch zu programmieren. Das wird eine kleine Parameterschlacht, weil es hier so viele Möglichkeiten gibt.
Andreas
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

Re: File IO mit dem Windows-API?

  Alt 11. Jun 2008, 13:37
Zitat von shmia:
Wenn man den optimalen Durchsatz erreichen möchte, dann muss man selbst (*) puffern und eine Puffergrösse von ~ 2 KB bereithalten.
am optimalsten kommt man wohl eher, wenn man die WindowsCache umgeht und selbst einen passenden Puffer einrichtet, aber da müßte man ganz genau darauf achten, wie groß der Puffer ist, da er dann an die Sektorgröße des Datenträgers angepaßt werden muß (und am Besten noch auf die Größen von z.B. Festplatttencache und Co.) und wenn man dann auch noch dafür sorgt, daß der eigene Cachespeicher nicht in die Fänge der Auslagerungsdatei gerät ... also nicht grad einfach (ich spiele ja nicht umsonst, schon seit sehr langer Zeit, mit meinem FileSplitter rum)

für eine eigene Puffergröße macht sich 16/32/64 KB wohl schon recht optimal (jedenfalls sollten es immer ganze Vielfache von 512 Byte und bei CDs/DVDs 2 KB sein) ... unter 8 KB würde ich bei den Cache und Clustergrößen aktueller Hardware und bei den großen Dateien nicht gehn ... und mehr als 8 MB wirken sich oftmals auch wieder als nachteilig aus.


Also abgesehn von den kleinen Dateien macht sich anscheinend das direkte arbeiten mit der WinAPI (oder halt der kleine Umweg über shmias TSeqFileStream) recht gut.
Ich würde jetzt aber erstmal keine extrabehandlung für die paar kleinen Dateichen vorsehn.
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.
  Mit Zitat antworten Zitat
HHick123

Registriert seit: 23. Jul 2007
73 Beiträge
 
#20

Re: File IO mit dem Windows-API?

  Alt 11. Jun 2008, 17:08
Zitat von shmia:
Die beiden Aufrufe für CreateFile() wären noch zu programmieren. Das wird eine kleine Parameterschlacht, weil es hier so viele Möglichkeiten gibt.
Hallo Leute, ich hab' mal versucht, diesen Vorschlag (Ableiten von THandleStream) etwas zu konkretisieren. Ich würde ich mir das ungefähr so vorstellen:
Delphi-Quellcode:
program a;

uses
  Classes, Windows, SysUtils, RTLConsts;

type
  TSeqFileStream=class(THandleStream)
public
  constructor Create(const FileName: string; Mode: Word);
  destructor Destroy; override;
end;

constructor TSeqFileStream.Create(const FileName: string; Mode: Word);
const
  AccessMode: array[0..2] of LongWord=
  (GENERIC_READ,GENERIC_WRITE,GENERIC_READ or GENERIC_WRITE);
  ShareMode: array[0..4] of LongWord=
  (0,0,FILE_SHARE_READ,FILE_SHARE_WRITE,FILE_SHARE_READ or FILE_SHARE_WRITE);
var
  fo:integer;
begin
  if Mode=fmCreate then
  begin
    fo:=CreateFile(PChar(FileName),GENERIC_READ or GENERIC_WRITE, 0, nil,
    CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN {<--!!}, 0);
    inherited Create(fo);
    if FHandle<0 then raise EFCreateError.CreateResFmt(@SFCreateError, [FileName]);
  end
  else
  begin
    fo:=-1;
    if ((Mode and 3)<=fmOpenReadWrite)
    and ((Mode and $F0)<=fmShareDenyNone) then
    fo:=CreateFile(PChar(FileName),
    AccessMode[Mode and 3], ShareMode[(Mode and $F0) shr 4], nil,
    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN {<--!!}, 0);
    inherited Create(fo);
    if FHandle<0 then raise EFOpenError.CreateResFmt(@SFOpenError, [FileName])
  end
end;

destructor TSeqFileStream.Destroy;
begin
  if FHandle>=0 then FileClose(FHandle);
  inherited Destroy;
end;

end.
Den Parameterkrieg hab' ich mich mal an TFileStream gehalten und FILE_FLAG_SEQUENTIAL_SCAN hinzugefügt, aber es ist wirklich wild!! Eine simples "case Mode of" zur Parameterkonvertierung hätte ich eigentlich schöner gefunden... Leider komm' ich heute nicht mehr zum Testen.

Viele Grüße,
Helmut
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 3     12 3      


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 12:40 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