Tavallisten PDF-tiedostojen (1 Mt - 10 Mt) käsittely Delphissä on suoraviivaista käyttämällä vakiomuotoisia virtaluokkia, kuten TFileStream tai TMemoryStream. Kun tehtävänäsi on kuitenkin käsitellä gigatavuluokan PDF-tiedostoja, kuten massiivisia teknisiä CAD-kaavioita, korkearesoluutioisia paikkatietokarttoja tai kertyneitä oikeudellisia arkistoja, vakiomuotoiset muistinvaraustekniikat hajoavat nopeasti.
Jos lataat 2 Gt:n PDF-tiedoston TMemoryStream-virtaan 32-bittisessä Delphi-sovelluksessa, törmäät välittömästi EOutOfMemory-poikkeukseen. Jopa 64-bittisissä sovelluksissa tämä aiheuttaa vakavia sivuvirheitä ja pysäyttää palvelimen kokonaan. Tässä artikkelissa tutkimme, kuinka massiivisten tiedostojen I/O-suorituskykyä voidaan optimoida käyttämällä muistikartoitettuja tiedostoja (Memory-Mapped Files).
Vakiovirtojen ongelma
Kun käytät TMemoryStream.LoadFromFile-metodia, käyttöjärjestelmä lukee tiedoston levyltä, varaa peräkkäistä RAM-muistia ja kopioi datan sinne. 2 Gt:n tiedoston kohdalla tämä tuhlaa 2 Gt fyysistä RAM-muistia ja vie huomattavasti aikaa pelkkään levynlukusilmukkaan.
Jopa TFileStream-luokan käyttö voi olla ongelmallista, jos hyppäät tiedostossa usein paikasta toiseen (esim. PDF:n XRef-taulukon jäsentäminen tiedoston lopussa ja sitten hyppääminen ympäri tiedostoa siroteltuihin objekteihin). Jatkuvat Seek- ja Read-kutsut johtavat suuriin ytimen siirtymien yleiskustannuksiin.
Ratkaisu: Muistikartoitetut tiedostot
Muistikartoitus (Windows-sovellusliittymän funktioiden CreateFileMapping ja MapViewOfFile kautta) pyytää käyttöjärjestelmää kartoittamaan tiedoston suoraan sovelluksen virtuaaliseen osoiteavaruuteen. Saat osoittimen dataan, ja Windowsin virtuaalimuistinhallinta huolehtii datan sivuttamisesta fyysiseen RAM-muistiin ja sieltä pois tiukasti sitä mukaa, kun käytät sitä.
Näin voit toteuttaa suuren suorituskyvyn muistikartoitetun tiedostonlukijan Delphissä PDF-jäsennystä varten:
uses
Winapi.Windows, System.SysUtils, System.Classes;
type
TMemoryMappedFileReader = class
private
FFileHandle: THandle;
FMappingHandle: THandle;
FDataPtr: Pointer;
FFileSize: Int64;
public
constructor Create(const FileName: string);
destructor Destroy; override;
property Data: Pointer read FDataPtr;
property Size: Int64 read FFileSize;
end;
constructor TMemoryMappedFileReader.Create(const FileName: string);
var
HighSize, LowSize: DWORD;
begin
// Open the file with read permissions
FFileHandle := CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if FFileHandle = INVALID_HANDLE_VALUE then
RaiseLastOSError;
// Get the 64-bit file size
LowSize := GetFileSize(FFileHandle, @HighSize);
FFileSize := (Int64(HighSize) shl 32) or LowSize;
// Create the mapping object
FMappingHandle := CreateFileMapping(FFileHandle, nil, PAGE_READONLY, HighSize, LowSize, nil);
if FMappingHandle = 0 then
RaiseLastOSError;
// Map the file into the virtual address space
FDataPtr := MapViewOfFile(FMappingHandle, FILE_MAP_READ, 0, 0, 0);
if FDataPtr = nil then
RaiseLastOSError;
end;
destructor TMemoryMappedFileReader.Destroy;
begin
if FDataPtr <> nil then UnmapViewOfFile(FDataPtr);
if FMappingHandle <> 0 then CloseHandle(FMappingHandle);
if FFileHandle <> INVALID_HANDLE_VALUE then CloseHandle(FFileHandle);
inherited;
end;
Miksi muistikartoitus on ylivoimainen PDF-jäsennyksessä
PDF on satunnaissaantimuoto. Jäsennin aloittaa lukemalla trailerin tiedoston lopussa, löytää XRef-taulukon ja hyppää sitten satunnaisesti tavusiirtymiin eri puolilla tiedostoa ladatakseen tiettyjä sanakirjoja ja virtoja.
Muistikartoituksen avulla:
- Nollakopiointi: Dataa ei kopioida ydintilasta käyttäjätilaan; luet suoraan käyttöjärjestelmän tiedostovälimuistista.
- Välitön lataus: 2 Gt:n PDF-tiedoston avaaminen kestää millisekunteja, koska levyltä ei tosiasiassa lueta dataa ennen kuin purat osoittimen viittauksen.
- Käyttöjärjestelmän hallitsema sivutus: Jos jäsentät 2 Gt:n tiedostosta vain 50 Mt dataa, käyttöjärjestelmä lataa fyysiseen RAM-muistiin vain kyseiset 50 Mt. Muistin kulutus pysyy pienenä.
Toteuttamalla mukautetun virtaluokan, joka perustuu muistikartoitettuihin tiedostoihin, Delphi-sovelluksesi pureskelee gigatavuluokan PDF-tiedostoja helposti, mikä parantaa dramaattisesti suorituskykyä ja skaalautuvuutta.
Huomautus: Optimoitu I/O-virtojen käsittely massiivisille asiakirjoille on sisäänrakennettu suoraan HotPDF VCL -komponenttiin.