Obrada standardnih PDF-ova (od 1MB do 10MB) u Delphiju je jednostavna pomoću standardnih klasa tokova kao što su TFileStream ili TMemoryStream. Međutim, kada dobijete zadatak da obradite PDF-ove gigabajtnih razmera, kao što su masivne inženjerske CAD šeme, geoprostorne mape visoke rezolucije ili akumulirane pravne arhive, standardne tehnike alokacije memorije brzo otkazuju.
Ako učitate PDF od 2GB u TMemoryStream u 32-bitnoj Delphi aplikaciji, odmah ćete naići na izuzetak EOutOfMemory. Čak i u 64-bitnim aplikacijama, ovo uzrokuje ozbiljne greške stranica i zaustavlja server. U ovom članku ćemo istražiti kako optimizovati I/O performanse za masivne datoteke pomoću datoteka mapiranih u memoriji (Memory-Mapped Files).
Problem sa standardnim tokovima
Kada koristite TMemoryStream.LoadFromFile, operativni sistem čita datoteku sa diska, alocira sekvencijalnu RAM memoriju i kopira podatke u nju. Za datoteku od 2GB, ovo troši 2GB fizičke RAM memorije i zahteva značajno vreme samo za petlju čitanja diska.
Čak i korišćenje TFileStream-a može biti problematično ako često skačete po datoteci (npr. parsiranje PDF XRef tabele na kraju datoteke, a zatim skakanje na objekte razbacane po datoteci). Kontinuirani pozivi Seek i Read rezultiraju visokim troškovima tranzicije kernela.
Rešenje: Datoteke mapirane u memoriji
Mapiranje memorije (preko Windows API funkcija CreateFileMapping i MapViewOfFile) traži od operativnog sistema da preslika datoteku direktno u virtuelni adresni prostor aplikacije. Dobijate pokazivač na podatke, a Windows menadžer virtuelne memorije obrađuje stranice podataka u fizičkoj RAM memoriji i iz nje tačno onako kako im pristupate.
Evo kako možete implementirati čitač datoteka mapiranih u memoriji visokih performansi u Delphiju za parsiranje PDF-a:
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;
Zašto mapiranje memorije dominira u parsiranju PDF-a
PDF je format sa nasumičnim pristupom. Parser počinje čitanjem trejlera na kraju datoteke, pronalazi XRef tabelu, a zatim nasumično skače na ofsete bajtova kroz datoteku kako bi učitao specifične rečnike i tokove.
Sa mapiranjem memorije:
- Bez kopiranja (Zero-Copy): Podaci se ne kopiraju iz prostora kernela u korisnički prostor; čitate direktno iz keš memorije datoteka operativnog sistema.
- Trenutno učitavanje: Otvaranje PDF-a od 2GB traje milisekundi, jer se zapravo nijedan podatak ne čita sa diska dok ne dereferencirate pokazivač.
- Strančenje kojim upravlja OS: Ako parsirate samo 50MB podataka iz datoteke od 2GB, OS učitava samo tih 50MB u fizičku RAM memoriju. Potrošnja memorije ostaje minimalna.
Implementacijom prilagođene klase tokova (stream-ova) podržane datotekama mapiranim u memoriji, vaša Delphi aplikacija može s lakoćom da obrađuje PDF-ove gigabajtnih razmera, dramatično poboljšavajući performanse i skalabilnost.
Napomena: Optimizovana obrada I/O tokova za masivne dokumente ugrađena je direktno u HotPDF VCL komponentu.