vložil Radek Červinka
9. ledna 2010 23:49
Delphi umožňuje různé možnosti pro přístup k souborům (TFileStream, BlockRead, Read atd), ale ještě je možné použít vysoce efektivní přístup přes soubory mapované do paměti (Memory mapped files) za pomoci Windows.
Tento přístup má několik výhod:
- vysoce efektivní přístup k souborům s HW podporou a cache Windows
- jednoduchá práce jako s pamětí
- efektivní nahodilý přístup (základ pro vícevláknové programy)
- přístup k obrovským souborům (do 4G pro 32 bit Delphi) a přitom soubor není zdaleka celý v paměti
Windows pro tyto případy nabízí tyto API funkce:
- CreateFileMapping
- OpenFileMapping
- MapViewOfFile
- MapViewOfFileEx
- UnmapViewOfFile
- FlushViewOfFile
- CloseHandle
To je jen pro úplnost, nemá cenu přímo používat API, když JCL nabízí pěkné zapouzdření přes následující třídy z jednotky JclFileUtils.pas:
- TJclMappedTextReader
- TJclFileMappingStream
- TJclFileMapping
Ukáži jednoduchý příklad, spočítání výskytu jednotlivých byte v zadaném souboru.
1program mmaptest;
2
3uses
4 SysUtils, JclFileUtils, Windows;
5
6var
7 oMappedFile: TJclMappedTextReader;
8 aiCounts: array [0..255] of integer;
9 i:Integer;
10 iTime, iEndTime:Cardinal;
11begin
12 for i:= 0 to 255 do
13 aiCounts[i] := 0;
14
15 iTime := GetTickCount;
16 oMappedFile := TJclMappedTextReader.Create('test.txt');
17 try
18 for i:= 0 to oMappedFile.Size - 1 do
19 inc(aiCounts[ord(oMappedFile.Chars[i])]);
20
21
22 iEndTime := GetTickCount;
23 finally
24 FreeAndNil(oMappedFile);
25 end;
26
27 writeln(Format('Cas: %dms ', [iEndTime - iTime]));
28
29 for i := 0 to 255 do
30 begin
31 if aiCounts[i] = 0 then
32 continue;
33 if i < 32 then
34 write(format('#%d: %d ', [i, aiCounts[i]]))
35 else
36 write(format('"%s": %d ', [Chr(i), aiCounts[i]]))
37 end;
38end.
Řádky 29 až do konce vypisují byte s nenulovým výskytem, ale klíčový řádek je 19, kde se využívá property TJclMappedTextReader.Chars. Tato property je implementovaná tak, že vrací byte (přetypovaný na AnsiChar - kompatibilita s Delphi 2009+, pro neunicode Delphi je to Char) z adresy namapovaného souboru. Jak vypadá?
1function TJclAnsiMappedTextReader.GetChars(Index: Integer): AnsiChar;
2begin
3 if (Index < 0) or (Index >= Size) then
4 raise EJclError.CreateRes(@RsFileIndexOutOfRange);
5 Result := AnsiChar(PByte(FContent + Index)^);
6end;
Je vidět, že je to opravdu jen přístup do paměti, tj. opravdu jednoduchá věc.
Ještě jednou opakuji, že výhodou této metody je opravdu jednoduchý přístup a zjednodušení programu (o zrychlení ani nemluvě).