![]() |
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 |
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:36 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