Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi File IO mit dem Windows-API? (https://www.delphipraxis.net/115006-file-io-mit-dem-windows-api.html)

HHick123 4. Jun 2008 10:53


File IO mit dem Windows-API?
 
Hallo Leute, gerade bin ich dabei, meine FileIO-Unit (WindowsXP, Delphi6, Turbo Delphi 2006), die ich in meinen Projekten einsetze, aufzupolieren, um den Programmablauf zu beschleunigen (vor allem für eine Simulation, die einige Tage läuft, und intensiv auf der Platte herumfuhrwerkt - einerseits soll's schneller gehen, andererseits tut mir die Platte leid).

Ich bin zumindest schon so weit, dass ich die Files in möglichst großen Portionen auf die Platte schreibe (z.B. mit Blockwrite), bzw. typisierte Dateien verwende, und in möglichst großen Portionen schreibe/lese. Das war schon mal ein großer Fortschritt.

Aber jetzt hab' ich mir das Tool "File Monitor" von Mark Russinovich (sysinternals) downgeloaded, wo man sehr gut mitschauen kann, was passiert.

Und: Ich kieg' die Kriese!!!!
Mein Code kann noch nicht optimal sein! Permanent sieht man auf low-level-Ebene, CLOSE gefolgt von OPEN für das gleiche File (diese korrespondieren NICHT zu reset/close auf high-level-Ebene. Dann werden Fileattribute, die ich setze (und die meiner Programmlogik damit bekannt sind), sofort danach, bei der nächsten Funktion der high-level-Ebene wieder abgefragt.
-> Alles in allem SEHR SEHR viel unnötiger Overhead.

Daher meine Frage:
Kann man den Filezugriff nicht etwas low-levelliger machen, vielleicht direkt mit dem Windows-API??? Gibt's da ev. ein Tutorial?

Viele Grüße,
Helmut

Bernhard Geyer 4. Jun 2008 10:56

Re: File IO mit dem Windows-API?
 
Hast du evtl. einen Virenscanner laufen der dir u.U. dazwischenpfuscht?

HHick123 4. Jun 2008 11:15

Re: File IO mit dem Windows-API?
 
Ich hab' im "File Monitor" als Filter meine exe-Datei eingestellt, sodass ich nur meine eigenen Filezugriffe sehe. -> Virenscanner ist auszuschliessen.

Das sieht z.B. so aus:

Engine_E.exe:OPEN 0001SAA4.FEA SUCCESS Options: Open Access: 00100100
Engine_E.exe:SET INFORMATION 0001SAA4.FEA SUCCESS FileBasicInformation
Engine_E.exe:CLOSE 0001SAA4.FEA SUCCESS
Engine_E.exe:OPEN 0001SAA4.FEA SUCCESS Options: Open Access: 0012019F
Engine_E.exe:READ 0001SAA4.FEA SUCCESS Offset: 0 Length: 131072
Engine_E.exe:CLOSE 0001SAA4.FEA SUCCESS
Engine_E.exe:OPEN 0001SAA4.FEA SUCCESS Options: Open Access: 0012019F
Engine_E.exe:QUERY INFORMATION 0001SAA4.FEA SUCCESS Length: 8592
Engine_E.exe:READ 0001SAA4.FEA SUCCESS Offset: 0 Length: 8592
Engine_E.exe:CLOSE 0001SAA4.FEA SUCCESS

Das wäre dann z.B. am selben File:
OPEN-SETINFORMATION-CLOSE-OPEN-READ-CLOSE-OPEN-QUERYINFORMATION-READ-CLOSE

Ok, ich hab' mich ja noch nicht näher damit beschäftigt, aber mein Gefühl sagt mir, das das low-level-mäßig noch optimierbar ist...

Extrem ist z.B. ein Aufruf von "ForceDirectories"...

Viele Grüße,
Helmut

HHick123 4. Jun 2008 11:29

Re: File IO mit dem Windows-API?
 
Uuups, hab' gerade einen Fehler in meiner Programmlogik entdeckt, und zwar, dass ich das Read-only-Attribut in obigem Posting unnötigerweise auch für Files zurücksetze (mit SetFileAttributes), die ich eh' nur lesen will! Manchmal denk' ich erst beim Fragen nach ;-).... Ok, ein Teil des Overheads ist von mir..... ;-)

Dennoch wär' ich sehr daran interessiert, wie der File-Zugriff auf der Ebene, die das Tool "File Monitor" anzeigt, zu realisieren wäre...

Grüße, Helmut

p80286 4. Jun 2008 12:22

Re: File IO mit dem Windows-API?
 
Hallo HHick123,

ich rate Dir Deine Zeit nicht damit zu verschwenden. Zu Zeiten von DOS und TurboPascal hat es noch richtig was gebracht daran zu drehen, ab windowsNT hab ich es aufgegeben Puffergrößen zu optimieren. Natürlich ist es kein Fehler Puffer mit der Größe Satzlänge*n zu nutzen, aber Windows weiß schon was gut für Dich ist. Im allgemeinen bist Du mit dem Tfilestream gut bedient. Das alte blockwrite hat manchmal Vorteile, aber umgekehrt gilt das auch und ich habe noch nicht herausbekommen woran es liegt. Die Unterschiede sind so gering, daß es sich auch nicht lohnt darüber zu grübeln.

Überprüf lieber Dein Programm ob Du Daten nicht mehrfach einliest, oder in zu kleinen Häppchen schreibst, da ist viel mehr herauszuholen.

Gruß
K-H

gammatester 4. Jun 2008 12:45

Re: File IO mit dem Windows-API?
 
Bist Du sicher, daß sich die IO-Aktionen nicht auf die Engine_E.exe selbst beziehen? ZB Resourcen laden etc? Da gäbe es dann auch nicht viel zu optimieren.

Gruß Gammatester

Zacherl 4. Jun 2008 13:22

Re: File IO mit dem Windows-API?
 
Jap entweder TFileStream oder manuell über die API mittels:

* CreateFile
* SetFilePointer
* WriteFile
* ReadFile
* CloseHandle

HHick123 4. Jun 2008 14:11

Re: File IO mit dem Windows-API?
 
Hi, nachdem ich jetzt in meiner Programmlogik noch Optimierungspotential gefunden habe, bin ich schon kleinlauter....

CreateFile etc. schau ich mir mal bei Gelegenheit genauer in der msdn an, sieht relativ kompliziert aus...

TFileStream klingt sehr interessant, hab' ich bis jetzt noch nicht benutzt...

I.a. handelt es sich bei mir um Files, die jeweils einen Datentyp hintereinander beinhalten, z.B. lauter smallints oder lauter singles. Momentan hab' ich meine Unit so geschrieben, dass das sie im interface Funktionen bereitstellt, um
- die Datei zu öffnen
- einen single/smallint/byte zu lesen (oder eof zurückmelden)
- die Datei schliesst.

In Wirklichkeit liest die Unit aber bei Bedarf einen ganzen Block (konfigurierbarer Größe) von smallints ein und gibt mit nur einen einzigen zurück, die anderen merkt sie sich fürs nächste mal.

Das Problem dabei ist aber: Wenn die Dateilänge nicht ein ganzzahliges Vielfaches der Blockgröße ist, mach ich das momentan so, dass ich die Datei schliesse, anders typisiert wieder öffne und den Rest einlese (bis vor kurzem noch byte für byte, aber das hab' ich mittlerweile auch schon verbessert, und lese in einem Rutsch den Rest ein). Trotzdem ist's noch nicht optimal.

Wie ist das mit TFileStream? Liest das "auf Vorrat" aus der Datei bzw. schreibt es einen ganzen Block auf einmal hinaus, auch wenn ich es hintereinander z.B. mit einzelnen singles füttere? Wenn ja, dann wäre das vermutlich ein guter Ersatz für meine ganze Unit...

Viele Grüße,
Helmut

p80286 4. Jun 2008 17:16

Re: File IO mit dem Windows-API?
 
Hallo HHick123,

ich hab da etwas aus meinen alten Beständen herausgekramt (ursprünglich TP mit blockread):
Delphi-Quellcode:
procedure PPADATENLESEN(efile:string);
const
  lies=128;
  sl=sizeof(tpsatz1);   {satzlänge=sizeof(tpsatz1)}
var
  pp     : tfilestream;
  buffer : packed array [1..lies,1..sl] of byte;
  gelesen : integer;
  i      : integer;
begin
  erstpasatz:=nil;
  leztpasatz:=nil;
  pp:=tfilestream.create(efile,fmopenread or fmsharedenywrite);
   while pp.position<pp.size do begin
    gelesen:=pp.Read(buffer,lies*sl);
    for i:=1 to gelesen div sl do begin
      new(actpasatz);
      move(buffer[i],actpasatz^.satz,sizeof(tpsatz1));
      actpasatz^.next:=nil;
      actpasatz^.last:=leztpasatz;
      if erstpasatz<>nil then
        leztpasatz^.next:=actpasatz
      else
        erstpasatz:=actpasatz;
      leztpasatz:=actpasatz;
    end;
  end;{while-----------------------}
  pp.free;
end;
wahrscheinlich gibts in der Zwischenzeit elegantere Lösungen, aber das Teil funktioniert seit ein paar Jahren.

Gruß
K-H

HHick123 9. Jun 2008 12:46

Re: File IO mit dem Windows-API?
 
Danke, p80268, ausgehend davon hab' ich ein paar Experimente mit TFileStream gemacht.

Also, prinzipiell scheint TFileStream etwa das zu bieten, was ich suche.
Demgemäß hab' ich den Thread ev. in der falschen Rubrik (Windows API) gepostet. :|

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, die für mich einerseits nicht ganz nachvollziehbar war, und andererseits aber anscheinend auch nicht so gut funktioniert, dass man sich die Bufferung im eigenen Programm überhaupt sparen könnte. Im speziellen: Wenn man z.B. byte für byte einliest, so greift er auch byte für byte auf die Platte zu, und liest nicht etwa voraus, was dann extrem langsam ist! Wenn man einen ganzen Buffer einliest, dann greift er unter umständen in anders großen Häppchen (z.B. einem größeren als der Buffer) auf die Platte zu, leider weiss man dann dabei aber nie genau, welche Buffergröße (ein Problem könnte sich z.B. ergeben, wenn ich eine extrem große Datei "ruckfrei" streamen möchte und TFileStream das "Häppchen" viel zu groß wählt) :?

Grüße,
Helmut

shmia 9. Jun 2008 13:55

Re: File IO mit dem Windows-API?
 
Zitat:

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

HHick123 10. Jun 2008 13:24

Re: File IO mit dem Windows-API?
 
Liste der Anhänge anzeigen (Anzahl: 2)
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

Apollonius 10. Jun 2008 13:29

Re: File IO mit dem Windows-API?
 
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.

himitsu 10. Jun 2008 13:38

Re: File IO mit dem Windows-API?
 
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?

HHick123 10. Jun 2008 15:52

Re: File IO mit dem Windows-API?
 
Zitat:

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:

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:

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 :-D

Zitat:

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

shmia 10. Jun 2008 16:24

Re: File IO mit dem Windows-API?
 
Zitat:

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

HHick123 10. Jun 2008 16:53

Re: File IO mit dem Windows-API?
 
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

shmia 10. Jun 2008 17:57

Re: File IO mit dem Windows-API?
 
Zitat:

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.

himitsu 11. Jun 2008 12:37

Re: File IO mit dem Windows-API?
 
Zitat:

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.

HHick123 11. Jun 2008 16:08

Re: File IO mit dem Windows-API?
 
Zitat:

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

HHick123 12. Jun 2008 14:25

Re: File IO mit dem Windows-API?
 
Liste der Anhänge anzeigen (Anzahl: 4)
So, jetzt hab' ich 's ausprobiert: Habe die oben gepostete Klasse TSeqFileStream in die weiter oben gepostete Unit MyFastFile.pas eingebaut (MySeqFastFile.pas) und mit PerformanceCounter (MyDebugTiming.pas) Zeitmessungen bei verschiedenen Filegrößen gemacht (Projekt anbei).

Tja. Leider noch kein Durchbruch....:
  • (2ms) MyFastFile.pas (200 byte)- schreiben
    (2ms) MySeqFastFile.pas (200 byte)- schreiben
    (0ms) MyFastFile.pas (200 byte)- lesen
    (0ms) MySeqFastFile.pas (200 byte)- lesen
    (2ms) MyFastFile.pas (2000 byte)- schreiben
    (3ms) MySeqFastFile.pas (2000 byte)- schreiben
    (0ms) MyFastFile.pas (2000 byte)- lesen
    (0ms) MySeqFastFile.pas (2000 byte)- lesen
    (4ms) MyFastFile.pas (20000 byte)- schreiben
    (4ms) MySeqFastFile.pas (20000 byte)- schreiben
    (0ms) MyFastFile.pas (20000 byte)- lesen
    (1ms) MySeqFastFile.pas (20000 byte)- lesen
    (13ms) MyFastFile.pas (200000 byte)- schreiben
    (13ms) MySeqFastFile.pas (200000 byte)- schreiben
    (3ms) MyFastFile.pas (200000 byte)- lesen
    (6ms) MySeqFastFile.pas (200000 byte)- lesen
    (95ms) MyFastFile.pas (2000000 byte)- schreiben
    (99ms) MySeqFastFile.pas (2000000 byte)- schreiben
    (30ms) MyFastFile.pas (2000000 byte)- lesen
    (53ms) MySeqFastFile.pas (2000000 byte)- lesen
    (1245ms) MyFastFile.pas (20000000 byte)- schreiben
    (965ms) MySeqFastFile.pas (20000000 byte)- schreiben
    (305ms) MyFastFile.pas (20000000 byte)- lesen
    (536ms) MySeqFastFile.pas (20000000 byte)- lesen
    (10259ms) MyFastFile.pas (200000000 byte)- schreiben
    (9314ms) MySeqFastFile.pas (200000000 byte)- schreiben
    (12835ms) MyFastFile.pas (200000000 byte)- lesen
    (19656ms) MySeqFastFile.pas (200000000 byte)- lesen
Meist ist TSeqFileStream sogar etwas langsamer...
Hmm...

Viele Grüße,
Helmut

himitsu 12. Jun 2008 14:39

Re: File IO mit dem Windows-API?
 
hast du mal einen Speedtest deiner Festplatte gemacht?

Zitat:

(12835ms) MyFastFile.pas (200000000 byte)- lesen
sind ja auch immerhin schonmal fast 15 MB/s


diese Woche wird es nichts mehr (keine Zeit), aber ich könnte in den nächten 1-2 Wochen man versuchen eine NonBuffered-Variante mit eigener Cache (von TSeqFileStream) zu basteln.
(weiß ja nicht, ob's dir helfen würde, mal in meine alten Hier im Forum suchenFileSplitter-QuellCodes rein zu gucken)

HHick123 12. Jun 2008 16:31

Re: File IO mit dem Windows-API?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

hast du mal einen Speedtest deiner Festplatte gemacht?
Hab' grad einen Test mit hdtune gemacht (screenshot anbei), wobei da die eine Achsenbeschriftung leider fehlt (ich nehme an, es ist die Puffergröße in kB?).
Jedenfalls ab 32 (kB?) aufwärts liegt es etwa bei 35 MB/s bei Filegröße von 256 MB (auch bei 128MB).

Zitat:

Zitat:

(12835ms) MyFastFile.pas (200000000 byte)- lesen
sind ja auch immerhin schonmal fast 15 MB/s
Tja, so schlecht ist es ja eigentlich eh' nicht. Was ich mir gut vorstellen könnte, ist, dass mich die vielen Funktionsaufrufe (für jedes SmallInt einer) ausbremsen. :gruebel:

Zitat:

diese Woche wird es nichts mehr (keine Zeit), aber ich könnte in den nächten 1-2 Wochen man versuchen eine NonBuffered-Variante mit eigener Cache (von TSeqFileStream) zu basteln.
Wär natürlich super, und würd' mich sehr interessieren! Aber mach' Dir keinen Stress..., das Thema hier im Thread eilt eigentlich überhaupt nicht...

Zitat:

(weiß ja nicht, ob's dir helfen würde, mal in meine alten FileSplitter -QuellCodes rein zu gucken)
Werd' ich machen!

Viele Grüße, Helmut

HHick123 26. Jun 2008 18:42

Re: File IO mit dem Windows-API?
 
Zitat:

Funktionsaufrufe (für jedes SmallInt einer) ausbremsen.
Die Funktionsaufrufe scheinen es nicht zu sein, was ich dadurch gecheckt hab, dass ich die Funktionalität in der Funktion weggelassen hab' und mit exit gleich wieder zurückgesprungen bin -> geht dann relativ blitzschnell, ausser der Compiler hat das durchschaut und meine Funktion gleich "wegoptimiert", das könnte natürlich sein....

Vjay 27. Jun 2008 13:33

Re: File IO mit dem Windows-API?
 
Zu deinem Prozessmonitortest ganz am Anfang noch eine Anmerkung: Auch wenn du den Filter auf deine Applikation gesetzt hast, kann es durchaus noch sein, dass du Antivirusausgaben in der Liste hast.
Irgendwie wird ein Antivirus deinen Prozess auch hooken denke ich mal.

HHick123 30. Jun 2008 08:46

Re: File IO mit dem Windows-API?
 
Zitat:

Auch wenn du den Filter auf deine Applikation gesetzt hast, kann es durchaus noch sein, dass du Antivirusausgaben in der Liste hast.
Tja, kann schon sein, werd' mal bei Gelegenheit probieren, den Virenscanner abzuschalten und dann die Zeit messen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:07 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