Pošten load/save benchmark za PDF Library for Delphi meri LoadFromFile i SaveToFile pomoću QueryPerformanceCounter-a, čuva sirove tick-ove i frekvenciju brojača, pokreće baseline i kandidata u naizmeničnim parovima A/B, B/A, A/B, odbija da krene dok je CPU opterećenje iznad 25%, odbacuje svaki rezultat čiji range-to-median raspon prelazi 15%, i baca svako merenje čiji sačuvani PDF padne strukturnu, render ili semantičku validaciju. Ta lista deluje kao birokratija dok prvi put ne ispari tvrdnja „20% brže” pri ponovnom pokretanju. Dalje sledi kako je do tamo stigao namenski corpus probe i njegov comparison runner, uključujući i prolaz u kome je mašina jednostavno bila prezauzeta da bi se išta merilo, pa je harness ispravno to i rekao
Zašto benchmark za PDF u Delphi-ju javlja nula sekundi?
PDF benchmark učitavanja javlja nula sekundi kada njegov sat tikta grublje od operacije koju meri, a GetTickCount64 je baš takav sat: vraća milisekunde, ali na Windows-u napreduje tek kad se zapali sistemski timer interrupt, obično na svakih 15,6 ms. FPC port demo programa za benchmark ogromnih fajlova u PDF Library for Delphi koristio je njega jer TStopwatch nije dostupan u tom toolchain-u, a proteklo vreme upisuje na tri decimale. Učitavanje malog CAD crteža ili kratkog tagged dokumenta završi se duboko unutar jednog tajmerskog koraka, pa je demo ponekad štampao 0.000 za učitavanje koje je očigledno radilo pravi posao
function ElapsedSeconds(StartTick: QWord): Double;
begin
Result:= (GetTickCount64- StartTick)/ 1000.0;
end;
// unutar petlje operacije
Lib:= TPDFlib.Create;
try
Lib.OnProgress:= Reporter.Progress;
Started:= GetTickCount64;
LoadCode:= Lib.LoadFromFile(InputFile, Password);
...
Nula je gora od nepreciznog broja, jer se svako poređenje koje na njoj sagradite deli njome. Upareni comparison runner svaku granu sa nultim minimumom tretira kao neodlučujuću sa obrazloženjem „Nulta dužina trajanja sprečava smislenu proporciju”, što je ispravno odbijanje, ali znači i da su demo merenja ostavila rupu tačno tamo gde žive kratki fajlovi. Isti demo instalira i OnProgress callback, pa njegova vremena uključuju trošak callback-a koji čisto load/save merenje ne bi smelo da nosi, a arhivirani demo brojevi nisu zamenljivi ni sa čim što se izmeri kasnije
Merenje LoadFromFile i SaveToFile pomoću QueryPerformanceCounter-a
Namenski console probe, Tests/CorpusLoadSave.dpr, meri dve operacije po ulaznom fajlu pomoću QueryPerformanceCounter-a: LoadFromFile plus čitanje PageCount-a, i LoadFromFile plus PageCount plus SaveToFile. Svaka operacija dobija svežu TPDFlib instancu i bez progress callback-a, a konstruktor i destruktor instance stoje van merene regije, kao i CSV upis i sva validacija izlaza. Brojač se čita neposredno pre učitavanja i neposredno posle poslednjeg poziva biblioteke, a LastErrorCode se dohvata tek posle drugog čitanja
Lib:= TPDFlib.Create;
try
if not QueryPerformanceCounter(Started) then
raise Exception.Create('Performance counter unavailable');
Code:= Lib.LoadFromFile(WideString(SourceFile), '');
if Code= 1 then
begin
Pages:= Lib.PageCount;
if Save then Code:= Lib.SaveToFile(WideString(SavedFile));
end;
if not QueryPerformanceCounter(Finished) then
raise Exception.Create('Performance counter unavailable');
ErrorCode:= Lib.LastErrorCode;
finally
Lib.Free;
end;
Ticks:= Finished- Started;
if Ticks< 0 then
raise Exception.Create('Performance counter moved backwards');
Probe upisuje sirovi broj tick-ova i frekvenciju brojača pored izvedenih sekundi, formatirano sa devet decimala i fiksnom . decimalnom separacijom, pa svako može sam da preračuna količnik iz CSV-a umesto da mu veruje. Na FPC Win64 buildu CAD uzorak učitao se za 8.888 tick-ova na 10.000.000 tick-ova po sekundi, zapisano kao 0.000888800 sekundi — zapažanje koje bi stari tajmer zaokružio na nulu. Probe namerno ne seče kratke vrednosti, ne zamenjuje ih minimalnim trajanjem i ne oduzima procenjeni tajmerski overhead, a i dalje upisuje oba reda sa ne-nultim exit kodom kada poziv biblioteke padne. Devet cifara ipak nije tačnost: više upisanih decimala ne kaže ništa o ponovljivosti, pa bučna ili nulta zapažanja i dalje moraju da se odbace nizvodno
if not QueryPerformanceFrequency(Frequency) or (Frequency<= 0) then
raise Exception.Create('Performance counter frequency unavailable');
NumberFormat:= TFormatSettings.Create;
NumberFormat.DecimalSeparator:= '.';
...
Rows.Add(CSV(ExtractFileName(SourceFile))+ ','+ CSV(Operation)+ ','+
FormatFloat('0.000000000', Ticks/ Frequency, NumberFormat)+ ','+
IntToStr(Code)+ ','+ IntToStr(ErrorCode)+ ','+ IntToStr(Pages)+ ','+
IntToStr(Ticks)+ ','+ IntToStr(Frequency));
Šta poređenje load/save vremena za PDF čini pouzdanim?
Poređenje vremena između dva builda PDF Library for Delphi je pouzdano tek kad su redosled starta, uslovi starta i raspon sva tri kontrolisani i zabeleženi, pa comparison runner zakazuje bar tri para u redosledu A/B, B/A, A/B. Ako baseline uvek ide prvi, kandidat tiho dobija topliji file cache i drugačije termičko stanje; naizmeničan redosled tu pristrasnost raspršuje na obe grane umesto da je pripise jednoj. Pre svake grane runner hešuje kompletan ulazni fajl SHA-256, čime i proverava da se ništa nije promenilo i unapred čita iste bajtove za obe grane, a ponovo hešuje i dva izvršna fajla i validacione alate posle svakog prolaza da se rebuildovan binarni fajl ne bi ušunjao usred serije
Zatim runner jednom u sekundi uzorkuje CPU iskorišćenje cele mašine i granu pokreće tek kad uzorak padne na 25% ili ispod, uz čekanje od najviše 30 sekundi pre nego što pokušaj zabeleži kao odbačen. Ta kapija kontroliše uslov starta i ništa drugo: ne izoluje mašinu tokom prolaza, pa power stanje, termički throttling, pozadinski poslovi i OS keširanje i dalje mogu da pomeraju brojeve. Zato je drugi filter statistički u najprizemnijem smislu. Za svaku operaciju runner računa range podeljen sa median za baseline granu, kandidat granu i raspodelu uparenih kandidat/baseline odnosa, i ako bilo koji od ta tri prelazi 0,15, rezultat se označava kao noisy umesto da se prijavi kao nalaz
Zašto same-binary kontrola dokazuje ponovljivost, a ne brzinu?
Same-binary kontrola pokreće identične izvršne fajlove kao baseline i kandidata, pa odnos blizu 1,0 može samo da dokaže da se merenje ponavlja samo po sebi; nikada ne može da pokaže da je implementacija postala brža. Prva stroga kontrola 2026-09-21 koristila je high-resolution FPC Win64 probe protiv priznatog tagged vodiča od 70 stranica, i svih šest startova je odbačeno jer su CPU uzorci šetali od 26,5% do 93,8%. Izveštaj je sadržao neuspehe i nijedan agregat, što je tačno ishod koji želite kad je mašina zauzeta. Istog dana ponovljen pokušaj sa bajt-identičnim ulazima, istim probe izvršnim fajlom i nepromenjenim pragovima prihvatio je svih šest startova u roku od 3 sekunde; svaki range-to-median raspon pao je između 0,019 i 0,054, a mediani odnosa bili su 1,0084 za LoadFromFile i 0,9872 za LoadFromFile + SaveToFile
Taj par brojeva utvrđuje kvalifikovan prozor posmatranja i ništa više. Kada se dva binarna fajla razlikuju, stabilan prolaz se označava kao deskriptivno poređenje, uz eksplicitnu napomenu da su odnosi zapažanja, a ne statistička značajnost ni tvrdnja o ubrzanju. Disciplina je najvažnija kad validirate ciljane optimizacije poput onih opisanih u tekstu o profilisanju PDF Library for Delphi i zameni hot putanja hash indeksima: profiler vam kaže gde vreme odlazi, ali vam tek kontrolisan upareni prolaz na pravim dokumentima kaže da li je promena preživela kontakt sa celim pipeline-om. Još jedna granica koju vredi reći naglas — normal-save uključuje učitavanje, a peak working set koji runner beleži je na nivou procesa, pa nijedan deo toga nije memorija koja se može pripisati samom čuvanju
Tri izlazne kapije i matrica sa četiri kompajlera
Nijedno merenje PDF Library for Delphi se ne računa ako fajl koji je proizvelo ne prođe tri nezavisne kapije, jer čuvanje koje brzo upiše pokvaren PDF nije brže čuvanje. Benchmark prvo proverava da su obe operacije vratile 1 i prijavile priznati broj stranica, a zatim validira jedini sačuvani PDF ovim redosledom:
- Struktura: nezavisni PDF checker mora da prođe sačuvani fajl bez grešaka i upozorenja
- Render: svaka stranica se renderuje u svom podrazumevanom stanju, i skup per-page image SHA-256 mora tačno da se poklopi sa referentnim renderom priznatog izvora
- Nevidljiva semantika: posebno semantičko poređenje sa izvorom pokriva odabrane osobine koje pikseli ne mogu da pokažu, uključujući optional-content i measurement strukture unutar njihovog dokumentovanog obuhvata
Sa tim kapijama na mestu, puna lokalna corpus matrica pokrenula je probe na FPC Win32, FPC Win64, Delphi Win32 i Delphi Win64 preko 12 priznatih PDF-ova sa 1.612 izvornih stranica, što daje 48 sample/target parova i 6.448 validiranih izlaznih stranica bez ijedne odabrane semantičke razlike. Svih 96 merenja operacija zadržalo je pozitivne sirove vrednosti brojača konzistentne sa prijavljenim sekundama, i te vrednosti namerno se ne agregiraju u tabelu brzine između kompajlera, jer je matrica funkcionalni dokaz, a ne kontrolisano poređenje. Load/save putanja takođe ne tvrdi da dekoduje svaku embedded sliku, validira potpise, izvršava XFA ili sertifikuje PDF/UA; ako treba da ocenite rendering throughput a ne load/save trošak, bolja je polazna tačka paralelni rendering stranica i thread safety u PDF Library for Delphi
Praktičan zaključak je kratak: čuvajte sirove brojače, naizmenično menjajte redosled, kapijom kontrolišite start, odbijajte bučne raspone i nikad ne merite izlaz koji niste validirali. Ta pravila su ono što PDF Library for Delphi-u dozvoljava da kaže „nema merljive promene” sa istom sigurnošću kao „brže je”, a isti probe source se nepromenjen kompajlira na Delphi-ju i FPC-u za Win32 i Win64. Biblioteku, njen load/save API i podržane kompajlere možete pregledati na stranici proizvoda PDF Library for Delphi