![]() |
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 |
Re: File IO mit dem Windows-API?
Hast du evtl. einen Virenscanner laufen der dir u.U. dazwischenpfuscht?
|
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 |
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 |
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 |
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 |
Re: File IO mit dem Windows-API?
Jap entweder TFileStream oder manuell über die API mittels:
* CreateFile * SetFilePointer * WriteFile * ReadFile * CloseHandle |
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 |
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:
wahrscheinlich gibts in der Zwischenzeit elegantere Lösungen, aber das Teil funktioniert seit ein paar Jahren.
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; Gruß K-H |
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 |
Re: File IO mit dem Windows-API?
Zitat:
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 |
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 |
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. |
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.
![]() Mit den Flags ![]() ![]() ![]() PS: Zitat:
PSS: Zitat:
oder könnte man Teile komplett im RAM abarbeiten lassen? |
Re: File IO mit dem Windows-API?
Zitat:
Zitat:
Zitat:
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:
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 |
Re: File IO mit dem Windows-API?
Zitat:
Hier die Messungen: Zitat:
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. ![]() |
Re: File IO mit dem Windows-API?
Zitat:
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 |
Re: File IO mit dem Windows-API?
Zitat:
Dazu würde ich von der Klasse THandleStream ableiten:
Delphi-Quellcode:
Die beiden Aufrufe für CreateFile() wären noch zu programmieren. Das wird eine kleine Parameterschlacht, weil es hier so viele Möglichkeiten gibt.
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; |
Re: File IO mit dem Windows-API?
Zitat:
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. |
Re: File IO mit dem Windows-API?
Zitat:
Delphi-Quellcode:
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.
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. Viele Grüße, Helmut |
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....:
Hmm... Viele Grüße, Helmut |
Re: File IO mit dem Windows-API?
hast du mal einen Speedtest deiner Festplatte gemacht?
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. (weiß ja nicht, ob's dir helfen würde, mal in meine alten ![]() |
Re: File IO mit dem Windows-API?
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Jedenfalls ab 32 (kB?) aufwärts liegt es etwa bei 35 MB/s bei Filegröße von 256 MB (auch bei 128MB). Zitat:
Zitat:
Zitat:
Viele Grüße, Helmut |
Re: File IO mit dem Windows-API?
Zitat:
|
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. |
Re: File IO mit dem Windows-API?
Zitat:
|
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