Poctivý load/save benchmark pro PDF Library for Delphi měří LoadFromFile a SaveToFile přes QueryPerformanceCounter, drží syrové ticky a frekvenci čítače, pouští baseline a kandidáta ve střídavých párech A/B, B/A, A/B, odmítá startovat, když zátěž CPU sedí nad 25 %, odmítá jakýkoli výsledek, jehož rozpětí range-to-median přesáhne 15 %, a vyhazuje každé měření, jehož uložené PDF neprojde strukturální, renderovací ani sémantickou validací. Tenhle seznam působí byrokraticky, dokud se vám poprvé tvrzení „20 % rychleji“ nevypaří při opakovaném běhu. Následuje, jak se k tomu dedikovaná corpus sonda a její comparison runner dostaly, včetně běhu, kdy byla mašina prostě moc zaneprázdněná na to, aby se dalo cokoli změřit, a harness to správně řekl
Proč PDF benchmark v Delphi hlásí nulové sekundy?
PDF load benchmark hlásí nulové sekundy, když jeho hodiny tikají hruběji, než jak rychlá je měřená operace, a GetTickCount64 je přesně tenhle druh hodin: vrací milisekundy, ale na Windows postupuje jen když se spustí přerušení systémového časovače, obvykle každých 15,6 ms. FPC port demo benchmarku velkých souborů v PDF Library for Delphi ho použil, protože TStopwatch v té toolchain není k dispozici, a zaznamenává uplynulý čas na tři desetinná místa. Načtení malé CAD kresby nebo krátkého tagged dokumentu skončí pohodlně uvnitř jednoho kroku časovače, takže demo občas vytisklo 0.000 pro načtení, které zjevně dělalo skutečnou práci
function ElapsedSeconds(StartTick: QWord): Double;
begin
Result:= (GetTickCount64- StartTick)/ 1000.0;
end;
// uvnitř smyčky operace
Lib:= TPDFlib.Create;
try
Lib.OnProgress:= Reporter.Progress;
Started:= GetTickCount64;
LoadCode:= Lib.LoadFromFile(InputFile, Password);
...
Nula je horší než nepřesné číslo, protože každé srovnání, které na ní postavíte, dělí nulou. Párovaný comparison runner bere jakoukoli větev s nulovým minimem jako neprůkaznou s odůvodněním „Zero duration prevents a meaningful ratio“, což je správné odmítnutí, ale znamená to taky, že demo měření zanechalo díru přesně tam, kde žijí krátké soubory. Tentýž demo si instaluje i callback OnProgress, takže jeho časy zahrnují režii callbacku, kterou by čisté load/save měření nosit nemělo, a archivovaná demo čísla nejsou zaměnitelná s čímkoli, co změříte později
Měření LoadFromFile a SaveToFile přes QueryPerformanceCounter
Dedikovaná konzolová sonda Tests/CorpusLoadSave.dpr měří na každý vstupní soubor dvě operace přes QueryPerformanceCounter: LoadFromFile plus čtení PageCount a LoadFromFile plus PageCount plus SaveToFile. Každá operace dostane čerstvou instanci TPDFlib a žádný progress callback a konstruktor a destruktor instance sedí mimo měřenou oblast, stejně jako zápis CSV a veškerá validace výstupu. Čítač se čte bezprostředně před načtením a bezprostředně po posledním volání knihovny a LastErrorCode se tahá až po druhém čtení
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');
Sonda zapisuje syrový počet ticků a frekvenci čítače vedle odvozených sekund, formátovaných na devět desetinných míst s pevným desetinným oddělovačem ., takže si kdokoli může podíl z CSV přepočítat, místo aby mu věřil. Na FPC Win64 buildu se vzorek CAD načetl za 8 888 ticků při 10 000 000 ticků za sekundu, zapsáno jako 0.000888800 sekundy — pozorování, které by starý časovač zaokrouhlil na nulu. Sonda záměrně nezastřihává krátké hodnoty, nenahrazuje je minimální dobou ani neodečítá odhadnutou režii časovače a i když volání knihovny selže, zapíše oba řádky a skončí s nenulovým exit kódem. Devět číslic ale není přesnost: víc zaznamenaných desetinných míst neříká nic o opakovatelnosti a zašuměná či nulová pozorování je pořád potřeba odmítnout na výstupu
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));
Co dělá srovnání časů PDF load/save důvěryhodným?
Srovnání časů mezi dvěma buildy PDF Library for Delphi je důvěryhodné jen tehdy, když jsou pořadí startu, podmínky startu i rozptyl všechny kontrolované a zaznamenané, takže comparison runner naplánuje nejméně tři páry v pořadí A/B, B/A, A/B. Když se baseline pouští vždy první, tichou rukou předá kandidátovi teplejší file cache a jiný teplotní stav; střídání pořadí rozprostře tenhle bias přes obě větve místo připisování jedné. Před každou větví runner spočítá SHA-256 kompletního vstupního souboru, což ověří, že se nic nezměnilo, a zároveň předčte tytéž bajty pro obě větve, a po každém běhu znovu hashuje oba spustitelné soubory i validační nástroje, takže znovu sestavený binary se nemůže vetřít doprostřed série
Runner pak jednou za sekundu vzorkuje celostrojové vytížení CPU a větev spustí jen když vzorek spadne na 25 % a níž, s maximální čekací dobou 30 sekund, než pokus zaznamená jako odmítnutý. Tahle brána kontroluje podmínku startu a nic víc: neizoluje mašinu během běhu a stav napájení, termální throttling, práce na pozadí i caching operačního systému můžou čísla pořád hýbat. Druhý filtr je proto statistický v tom nejjednodušším smyslu. Pro každou operaci runner spočítá range dělený median pro větev baseline, větev kandidáta a rozdělení párovaných poměrů kandidát/baseline, a když kterýkoli ze tří přesáhne 0.15, výsledek se označí jako noisy místo reportování jako zjištění
Proč same-binary kontrola prokazuje opakovatelnost, ne rychlost?
Same-binary kontrola pouští identické spustitelné soubory jako baseline i kandidáta, takže poměr blízko 1.0 může prokázat jen to, že se měřicí uspořádání opakuje samo; nikdy neukáže, že implementace zrychlila. První přísná kontrola 2026-09-21 použila high-resolution FPC Win64 sondu proti přijaté tagged příručce o 70 stránkách a všech šest startů se odmítlo, protože vzorky CPU se pohybovaly od 26,5 % do 93,8 %. Report obsahoval selhání a žádné agregáty, což je přesně ten výstup, který chcete, když je mašina zaneprázdněná. Opakování v týž den s bajtově identickými vstupy, týmž probe spustitelným souborem a nezměněnými prahy přijalo všech šest startů do 3 sekund; každé rozpětí range-to-median skončilo mezi 0.019 a 0.054 a mediány poměrů byly 1.0084 pro LoadFromFile a 0.9872 pro LoadFromFile + SaveToFile
Ta dvojice čísel zakládá kvalifikované pozorovací okno a nic víc. Když se oba binary liší, stabilní běh se označí jako deskriptivní srovnání s explicitní poznámkou, že poměry jsou pozorování, ne statistická významnost ani tvrzení o zrychlení. Ta disciplína hraje největší roli, když validujete cílené optimalizace, jako jsou ty popsané v článku o profilování PDF Library for Delphi a nahrazování hot paths hash indexy: profiler vám řekne, kam čas teče, ale jen kontrolovaný párovaný běh na reálných dokumentech řekne, jestli změna přežila kontakt s celým pipeline. Ještě jedna hranice, kterou stojí za to vyslovit nahlas — normal-save zahrnuje načítání a peak working set, který runner zaznamenává, je celoprocessní, takže žádná z té paměti se nedá připsat samotnému ukládání
Tři výstupní brány a matice čtyř kompilátorů
Žádný čas PDF Library for Delphi se nepočítá, dokud soubor, který vytvořila, neprojde třemi nezávislými branami, protože ukládání, které rychle napíše rozbité PDF, není rychlejší ukládání. Benchmark nejdřív zkontroluje, že obě operace vrátily 1 a nahlásily přijatý počet stránek, a pak validuje jediné uložené PDF v tomhle pořadí:
- Struktura: nezávislý PDF checker musí projít uložený soubor bez chyb ani varování
- Renderování: každá stránka se vyrenderuje ve svém defaultním stavu a sada SHA-256 obrázků na stránku musí přesně sedět na referenční render přijatého zdroje
- Neverbální sémantika: samostatné sémantické srovnání se zdrojem pokrývá vybrané vlastnosti, které pixely neukážou, včetně optional-content a měřicích struktur v rámci jejich zdokumentovaného rozsahu
S těmito branami proběhla plná lokální corpus matice na FPC Win32, FPC Win64, Delphi Win32 a Delphi Win64 přes 12 přijatých PDF s 1 612 zdrojovými stránkami, což dalo 48 párů sample/target a 6 448 validovaných výstupních stránek bez žádných vybraných sémantických rozdílů. Všech 96 měření operací si podrželo kladné syrové hodnoty čítače konzistentní s hlášenými sekundami a tyhle hodnoty se záměrně neagregují do tabulky rychlosti napříč kompilátory, protože matice je funkční důkaz, ne kontrolované srovnání. Load/save cesta taky netvrdí, že dekóduje každý embedded obrázek, validuje podpisy, spouští XFA nebo certifikuje PDF/UA; pokud potřebujete soudit renderovací throughput místo nákladů load/save, lepším startem jsou concurrency omezení popsaná v článku o paralelním renderování stránek a thread safety v PDF Library for Delphi
Praktické ponaučení je krátké: držte syrové čítače, střídejte pořadí, bráňte start, odmítejte zašuměná rozpětí a nikdy neměřte výstup, který jste nevalidovali. Ta pravidla jsou to, co PDF Library for Delphi dovoluje říct „žádná měřitelná změna“ se stejnou jistotou jako „rychlejší“, a týž probe zdroj se nezměněný kompiluje na Delphi i FPC pro Win32 a Win64. Knihovnu, její load/save API a podporované kompilátory si můžete projít na stránce produktu PDF Library for Delphi